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))
11#include "hal/usb_serial_jtag_ll.h"
12#include "soc/interrupts.h"
17constexpr uint32_t TX_INTR_MASK = USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY;
20constexpr uint32_t RX_INTR_MASK = USB_SERIAL_JTAG_INTR_SERIAL_OUT_RECV_PKT;
23constexpr uint32_t ALL_INTR_MASK = TX_INTR_MASK | RX_INTR_MASK;
29void ESP32CDCJtagReadPort::OnRxDequeue(
bool in_isr) { owner_.DrainRxToQueue(in_isr); }
32ESP32CDCJtag::ESP32CDCJtag(
size_t rx_buffer_size,
size_t tx_buffer_size,
33 uint32_t tx_queue_size, UART::Configuration config)
34 : UART(&_read_port, &_write_port),
36 tx_slot_storage_(new uint8_t[tx_buffer_size * 2U]),
37 _read_port(rx_buffer_size, *this),
38 _write_port(tx_queue_size, tx_buffer_size)
40 ASSERT(tx_buffer_size > 0U);
42 tx_double_buffer_.Init({tx_slot_storage_, tx_buffer_size * 2U});
47 if (SetConfig(config_) != ErrorCode::OK)
53 if (InitHardware() != ErrorCode::OK)
60ErrorCode ESP32CDCJtag::SetConfig(UART::Configuration config)
62 if ((config.data_bits != 8) || (config.stop_bits != 1) ||
63 (config.parity != UART::Parity::NO_PARITY))
65 return ErrorCode::ARG_ERR;
81 const esp_err_t intr_ans = esp_intr_alloc(
82 ETS_USB_SERIAL_JTAG_INTR_SOURCE, ESP_INTR_FLAG_IRAM, IsrEntry,
this, &intr_handle_);
83 intr_installed_ = (intr_ans == ESP_OK);
86 intr_handle_ =
nullptr;
87 return ErrorCode::INIT_ERR;
90 usb_serial_jtag_ll_clr_intsts_mask(ALL_INTR_MASK);
91 usb_serial_jtag_ll_ena_intr_mask(RX_INTR_MASK);
98void IRAM_ATTR ESP32CDCJtag::IsrEntry(
void* arg)
100 auto* cdc =
static_cast<ESP32CDCJtag*
>(arg);
103 cdc->HandleInterrupt();
108ErrorCode IRAM_ATTR ESP32CDCJtag::WriteFun(WritePort& port,
bool in_isr)
111 return cdc->TryStartTx(in_isr);
115ErrorCode ESP32CDCJtag::ReadFun(ReadPort&,
bool) {
return ErrorCode::PENDING; }
118void IRAM_ATTR ESP32CDCJtag::ClearActiveTx() { tx_double_buffer_.ClearActive(); }
121void IRAM_ATTR ESP32CDCJtag::ClearPendingTx() { tx_double_buffer_.ClearPending(); }
124void IRAM_ATTR ESP32CDCJtag::ResetTxState(
bool)
126 tx_double_buffer_.Reset();
131bool IRAM_ATTR ESP32CDCJtag::DequeueTxToSlot(uint8_t* slot,
size_t& size,
132 WriteInfoBlock& info,
bool in_isr)
136 WriteInfoBlock peek_info = {};
137 if (write_port_->queue_info_->Peek(peek_info) != ErrorCode::OK)
142 if (peek_info.data.size_ > tx_double_buffer_.BufferSize())
148 if (write_port_->queue_data_->PopBatch(slot, peek_info.data.size_) != ErrorCode::OK)
153 if (write_port_->queue_info_->Pop(info) != ErrorCode::OK)
158 size = peek_info.data.size_;
163bool IRAM_ATTR ESP32CDCJtag::LoadActiveTxFromQueue(
bool in_isr)
165 if (tx_double_buffer_.HasActive())
170 size_t active_size = 0U;
171 WriteInfoBlock active_info = {};
172 if (!DequeueTxToSlot(tx_double_buffer_.ActiveBuffer(), active_size, active_info,
178 tx_double_buffer_.LoadActive(active_size, active_info);
183bool IRAM_ATTR ESP32CDCJtag::LoadPendingTxFromQueue(
bool in_isr)
185 if (tx_double_buffer_.HasPending())
190 size_t pending_size = 0U;
191 WriteInfoBlock pending_info = {};
192 if (!DequeueTxToSlot(tx_double_buffer_.PendingBuffer(), pending_size, pending_info,
198 tx_double_buffer_.LoadPending(pending_size, pending_info);
203bool IRAM_ATTR ESP32CDCJtag::StartPendingTxIfIdle(
bool in_isr)
205 if (tx_busy_.IsSet() || tx_double_buffer_.HasActive() ||
206 !tx_double_buffer_.HasPending())
211 if (!tx_double_buffer_.PromotePending())
216 return StartAndReportActive(in_isr);
220bool IRAM_ATTR ESP32CDCJtag::PumpTx(
bool)
222 while (tx_busy_.IsSet())
224 if (!tx_double_buffer_.HasActive() || (tx_double_buffer_.ActiveBuffer() ==
nullptr) ||
225 (tx_double_buffer_.ActiveOffset() >= tx_double_buffer_.ActiveLength()))
231 const size_t active_offset = tx_double_buffer_.ActiveOffset();
232 const uint32_t remain =
233 static_cast<uint32_t
>(tx_double_buffer_.ActiveLength() - active_offset);
234 const int written = usb_serial_jtag_ll_write_txfifo(
235 tx_double_buffer_.ActiveBuffer() + active_offset, remain);
241 tx_double_buffer_.SetActiveOffset(active_offset +
static_cast<size_t>(written));
242 if (tx_double_buffer_.ActiveOffset() >= tx_double_buffer_.ActiveLength())
253void IRAM_ATTR ESP32CDCJtag::PushRxBytes(
const uint8_t* data,
size_t size,
bool in_isr)
256 bool pushed_any =
false;
258 while (offset < size)
260 const size_t free_space = read_port_->queue_data_->EmptySize();
261 if (free_space == 0U)
266 const size_t chunk = std::min(free_space, size - offset);
267 if (read_port_->queue_data_->PushBatch(data + offset, chunk) != ErrorCode::OK)
278 read_port_->ProcessPendingReads(in_isr);
284void IRAM_ATTR ESP32CDCJtag::DrainRxToQueue(
bool in_isr)
286 if (rx_draining_.TestAndSet())
291 while (usb_serial_jtag_ll_rxfifo_data_available())
293 uint8_t rx_tmp[64] = {};
294 const int got = usb_serial_jtag_ll_read_rxfifo(rx_tmp,
sizeof(rx_tmp));
300 PushRxBytes(rx_tmp,
static_cast<size_t>(got), in_isr);
302 if (read_port_->queue_data_->EmptySize() == 0U)
308 rx_draining_.Clear();
312bool IRAM_ATTR ESP32CDCJtag::StartActiveTransfer(
bool in_isr)
314 if (!tx_double_buffer_.HasActive() || (tx_double_buffer_.ActiveBuffer() ==
nullptr) ||
315 (tx_double_buffer_.ActiveLength() == 0U))
320 if (tx_busy_.TestAndSet())
325 tx_double_buffer_.SetActiveOffset(0U);
326 usb_serial_jtag_ll_ena_intr_mask(TX_INTR_MASK);
327 (void)PumpTx(in_isr);
332bool IRAM_ATTR ESP32CDCJtag::StartAndReportActive(
bool in_isr)
334 if (!StartActiveTransfer(in_isr))
336 write_port_->Finish(in_isr, ErrorCode::FAILED, tx_double_buffer_.ActiveInfo());
342 write_port_->Finish(in_isr, ErrorCode::OK, tx_double_buffer_.ActiveInfo());
343 if (!tx_busy_.IsSet() && tx_double_buffer_.HasActive())
345 OnTxTransferDone(in_isr, ErrorCode::OK);
351void IRAM_ATTR ESP32CDCJtag::StopTxTransfer()
353 usb_serial_jtag_ll_txfifo_flush();
354 usb_serial_jtag_ll_disable_intr_mask(TX_INTR_MASK);
358void IRAM_ATTR ESP32CDCJtag::OnTxTransferDone(
bool in_isr, ErrorCode result)
360 Flag::ScopedRestore tx_flag(in_tx_isr_);
365 if ((result != ErrorCode::OK) && tx_double_buffer_.HasPending())
367 write_port_->Finish(in_isr, ErrorCode::FAILED, tx_double_buffer_.PendingInfo());
371 if (result != ErrorCode::OK)
377 if (!tx_double_buffer_.HasPending())
383 if (StartPendingTxIfIdle(in_isr))
385 if (!tx_double_buffer_.HasPending())
387 (void)LoadPendingTxFromQueue(in_isr);
398ErrorCode IRAM_ATTR ESP32CDCJtag::TryStartTx(
bool in_isr)
400 if (in_tx_isr_.IsSet())
402 return ErrorCode::PENDING;
405 if (!tx_busy_.IsSet() && !tx_double_buffer_.HasActive() &&
406 !tx_double_buffer_.HasPending())
408 if (!LoadActiveTxFromQueue(in_isr))
410 return ErrorCode::PENDING;
413 if (!StartActiveTransfer(in_isr))
416 return ErrorCode::FAILED;
419 if (!tx_busy_.IsSet() && tx_double_buffer_.HasActive())
421 OnTxTransferDone(in_isr, ErrorCode::OK);
424 return ErrorCode::OK;
427 if (tx_busy_.IsSet() && !tx_double_buffer_.HasPending())
429 (void)LoadPendingTxFromQueue(in_isr);
432 return ErrorCode::PENDING;
436void IRAM_ATTR ESP32CDCJtag::HandleInterrupt()
438 const uint32_t status = usb_serial_jtag_ll_get_intsts_mask();
440 const uint32_t rx_status = status & RX_INTR_MASK;
443 usb_serial_jtag_ll_clr_intsts_mask(rx_status);
444 DrainRxToQueue(
true);
447 const uint32_t tx_status = status & TX_INTR_MASK;
453 usb_serial_jtag_ll_clr_intsts_mask(tx_status);
455 Flag::ScopedRestore tx_flag(in_tx_isr_);
456 const bool was_busy = tx_busy_.IsSet();
458 if (was_busy && !tx_busy_.IsSet())
460 OnTxTransferDone(
true, ErrorCode::OK);
ErrorCode(* ReadFun)(ReadPort &port, bool in_isr)
Function pointer type for read notifications.
ErrorCode(* WriteFun)(WritePort &port, bool in_isr)
Function pointer type for write operations.
OwnerType * ContainerOf(MemberType *ptr, MemberType OwnerType::*member) noexcept
通过成员指针恢复其所属对象指针