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))
9#include "hal/usb_serial_jtag_ll.h"
10#include "soc/interrupts.h"
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;
31 owner_.ResumeRx(in_isr);
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)
40 ASSERT(rx_buffer_size > 0U);
41 ASSERT(tx_buffer_size > 0U);
42 ASSERT(tx_queue_size > 0U);
44 INIT_CRIT_SECTION_LOCK_RUNTIME(&irq_lock_);
46 ASSERT(IsConfigSupported(config));
47 [[maybe_unused]]
const auto init_hardware_result = InitHardware();
48 REQUIRE(init_hardware_result == ErrorCode::OK);
53ErrorCode ESP32CDCJtag::SetConfig(UART::Configuration config,
bool)
55 return IsConfigSupported(config) ? ErrorCode::OK : ErrorCode::ARG_ERR;
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)
69 intr_handle_ =
nullptr;
70 return ErrorCode::INIT_ERR;
73 DisableAndClearInterrupt(ALL_INTR_MASK);
74 EnableInterrupt(RX_INTR_MASK);
80void ESP32CDCJtag::IsrEntry(
void* arg)
82 auto* cdc =
static_cast<ESP32CDCJtag*
>(arg);
85 cdc->HandleInterrupt();
89void ESP32CDCJtag::WriteFun(WritePort& port,
bool in_isr)
92 cdc->service_.Invoke(EVENT_WRITE, in_isr, [cdc](uint32_t events,
bool owner_isr)
93 { cdc->ServiceEvents(events, owner_isr); });
96void ESP32CDCJtag::ResumeRx(
bool in_isr)
98 service_.Invoke(EVENT_RX_SPACE, in_isr, [
this](uint32_t events,
bool owner_isr)
99 { ServiceEvents(events, owner_isr); });
102void ESP32CDCJtag::ServiceEvents(uint32_t events,
bool in_isr)
104 if ((events & EVENT_TX_EMPTY) != 0U)
106 tx_empty_interrupt_armed_ =
false;
108 if ((events & (EVENT_RX_DATA | EVENT_RX_SPACE)) != 0U)
110 auto queue = _read_port.GetReadQueue(in_isr);
111 ServiceRx(queue, in_isr);
115 if ((events & (EVENT_WRITE | EVENT_TX_EMPTY)) != 0U)
121void ESP32CDCJtag::PushRxBytes(ReadPort::ReadQueue& queue,
const uint8_t* data,
122 size_t size,
bool in_isr)
124 [[maybe_unused]]
const auto push_batch_result = queue.PushBatch(data, size);
125 DEV_ASSERT_FROM_CALLBACK(push_batch_result == ErrorCode::OK, in_isr);
128void ESP32CDCJtag::DrainRxToQueue(ReadPort::ReadQueue& queue,
bool in_isr)
130 while (usb_serial_jtag_ll_rxfifo_data_available())
132 const size_t free_space = queue.EmptySize();
133 if (free_space == 0U)
138 uint8_t rx_tmp[ENDPOINT_SIZE] = {};
139 const size_t read_size = std::min(free_space,
sizeof(rx_tmp));
141 usb_serial_jtag_ll_read_rxfifo(rx_tmp,
static_cast<uint32_t
>(read_size));
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);
151void ESP32CDCJtag::ServiceRx(ReadPort::ReadQueue& queue,
bool in_isr)
153 DisableInterrupt(RX_INTR_MASK);
154 DrainRxToQueue(queue, in_isr);
156 if (queue.EmptySize() != 0U)
158 EnableInterrupt(RX_INTR_MASK);
162void ESP32CDCJtag::ArmTxEmptyInterrupt()
164 if (!tx_empty_interrupt_armed_)
166 EnableInterrupt(TX_INTR_MASK);
167 tx_empty_interrupt_armed_ =
true;
171void ESP32CDCJtag::DisarmTxEmptyInterrupt()
173 if (tx_empty_interrupt_armed_)
175 DisableAndClearInterrupt(TX_INTR_MASK);
176 tx_empty_interrupt_armed_ =
false;
180 ClearInterrupt(TX_INTR_MASK);
184void ESP32CDCJtag::FlushTxFifo()
186 if (tx_flush_pending_)
188 usb_serial_jtag_ll_txfifo_flush();
189 tx_flush_pending_ =
false;
193size_t ESP32CDCJtag::FillTxFifo(WritePort::WriteQueue& queue,
bool in_isr)
195 return queue.PopWithWriter(
197 [
this, in_isr](
const uint8_t* first,
size_t first_size,
const uint8_t* second,
198 size_t second_size) ->
size_t
200 auto write_span = [
this, in_isr](
const uint8_t* data,
size_t size) ->
size_t
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;
214 const size_t first_written = write_span(first, first_size);
215 if (first_written != first_size)
217 tx_flush_pending_ = tx_flush_pending_ || first_written != 0U;
218 return first_written;
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;
228void ESP32CDCJtag::ProgressTx(
bool in_isr)
230 if (!usb_serial_jtag_ll_txfifo_writable())
232 ArmTxEmptyInterrupt();
236 size_t accepted = 0U;
238 auto queue = _write_port.GetWriteQueue(in_isr);
242 DisarmTxEmptyInterrupt();
246 accepted = FillTxFifo(queue, in_isr);
251 ArmTxEmptyInterrupt();
255 if (_write_port.Size() == 0U)
257 if (usb_serial_jtag_ll_txfifo_writable())
260 DisarmTxEmptyInterrupt();
266 ArmTxEmptyInterrupt();
271 if (usb_serial_jtag_ll_txfifo_writable())
275 ArmTxEmptyInterrupt();
279void ESP32CDCJtag::HandleInterrupt()
281 uint32_t status = CaptureInterrupt(ALL_INTR_MASK);
284 const uint32_t handled = status & ALL_INTR_MASK;
290 uint32_t events = 0U;
291 if ((handled & RX_INTR_MASK) != 0U)
293 events |= EVENT_RX_DATA;
295 if ((handled & TX_INTR_MASK) != 0U)
297 events |= EVENT_TX_EMPTY;
300 service_.Invoke(events,
true, [
this](uint32_t owner_events,
bool owner_isr)
301 { ServiceEvents(owner_events, owner_isr); });
303 status = CaptureInterrupt(ALL_INTR_MASK);
307void ESP32CDCJtag::EnableInterrupt(uint32_t mask)
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_);
314void ESP32CDCJtag::DisableInterrupt(uint32_t mask)
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_);
321void ESP32CDCJtag::ClearInterrupt(uint32_t mask)
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_);
328void ESP32CDCJtag::DisableAndClearInterrupt(uint32_t mask)
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_);
336uint32_t ESP32CDCJtag::CaptureInterrupt(uint32_t mask)
338 esp_os_enter_critical_safe(&irq_lock_);
339 const uint32_t handled = usb_serial_jtag_ll_get_intsts_mask() & mask;
342 usb_serial_jtag_ll_disable_intr_mask(handled);
343 usb_serial_jtag_ll_clr_intsts_mask(handled);
345 esp_os_exit_critical_safe(&irq_lock_);
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.
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.
uint8_t stop_bits
停止位长度 / Number of stop bits