mirror of
https://github.com/esphome/esphome.git
synced 2026-09-11 23:37:34 +00:00
Merge branch 'rp2040-wrap-printf' into integration
This commit is contained in:
@@ -190,6 +190,11 @@ async def to_code(config):
|
||||
],
|
||||
)
|
||||
|
||||
# Wrap FILE*-based printf functions to eliminate newlib's _vfprintf_r
|
||||
# (~8.9 KB). See printf_stubs.cpp for implementation.
|
||||
for symbol in ("vprintf", "printf", "fprintf"):
|
||||
cg.add_build_flag(f"-Wl,--wrap={symbol}")
|
||||
|
||||
cg.add_platformio_option("board_build.core", "earlephilhower")
|
||||
cg.add_platformio_option("board_build.filesystem_size", "1m")
|
||||
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Linker wrap stubs for FILE*-based printf functions.
|
||||
*
|
||||
* The RP2040 Arduino framework and libraries may reference printf(),
|
||||
* vprintf(), and fprintf() which pull in newlib's _vfprintf_r (~8.9 KB).
|
||||
* ESPHome never uses these — all logging goes through the logger component
|
||||
* which uses snprintf/vsnprintf, so the libc FILE*-based printf path is
|
||||
* dead code.
|
||||
*
|
||||
* These stubs redirect through vsnprintf() (which is already in the binary)
|
||||
* and fwrite(), allowing the linker to dead-code eliminate _vfprintf_r.
|
||||
*
|
||||
* Saves ~8.9 KB of flash.
|
||||
*/
|
||||
|
||||
#if defined(USE_RP2040) && !defined(USE_FULL_PRINTF)
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
namespace esphome::rp2040 {}
|
||||
|
||||
static constexpr size_t PRINTF_BUFFER_SIZE = 512;
|
||||
|
||||
// These stubs are essentially dead code at runtime — ESPHome uses its own
|
||||
// logging through snprintf/vsnprintf, not libc printf.
|
||||
// The buffer overflow check is purely defensive and should never trigger.
|
||||
static int write_printf_buffer(FILE *stream, char *buf, int len) {
|
||||
if (len < 0) {
|
||||
return len;
|
||||
}
|
||||
size_t write_len = len;
|
||||
if (write_len >= PRINTF_BUFFER_SIZE) {
|
||||
fwrite(buf, 1, PRINTF_BUFFER_SIZE - 1, stream);
|
||||
abort();
|
||||
}
|
||||
if (fwrite(buf, 1, write_len, stream) < write_len || ferror(stream)) {
|
||||
return -1;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
// NOLINTBEGIN(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp,readability-identifier-naming)
|
||||
extern "C" {
|
||||
|
||||
int __wrap_vprintf(const char *fmt, va_list ap) {
|
||||
char buf[PRINTF_BUFFER_SIZE];
|
||||
return write_printf_buffer(stdout, buf, vsnprintf(buf, sizeof(buf), fmt, ap));
|
||||
}
|
||||
|
||||
int __wrap_printf(const char *fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
int len = __wrap_vprintf(fmt, ap);
|
||||
va_end(ap);
|
||||
return len;
|
||||
}
|
||||
|
||||
int __wrap_fprintf(FILE *stream, const char *fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
char buf[PRINTF_BUFFER_SIZE];
|
||||
int len = write_printf_buffer(stream, buf, vsnprintf(buf, sizeof(buf), fmt, ap));
|
||||
va_end(ap);
|
||||
return len;
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
// NOLINTEND(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp,readability-identifier-naming)
|
||||
|
||||
#endif // USE_RP2040 && !USE_FULL_PRINTF
|
||||
@@ -8,13 +8,97 @@
|
||||
namespace esphome::usb_uart {
|
||||
|
||||
using namespace bytebuffer;
|
||||
/**
|
||||
* CH34x
|
||||
*/
|
||||
|
||||
struct CH34xEntry {
|
||||
uint16_t pid;
|
||||
uint8_t byte_idx; // which status.data[] byte to inspect
|
||||
uint8_t mask; // bitmask applied before comparison
|
||||
uint8_t match; // 0xFF = wildcard (default/fallthrough for this PID)
|
||||
CH34xChipType chiptype;
|
||||
const char *name;
|
||||
uint8_t num_ports;
|
||||
};
|
||||
|
||||
static const CH34xEntry CH34X_TABLE[] = {
|
||||
{0x55D2, 1, 0xFF, 0x41, CHIP_CH342K, "CH342K", 2},
|
||||
{0x55D2, 1, 0xFF, 0xFF, CHIP_CH342F, "CH342F", 2},
|
||||
{0x55D3, 1, 0xFF, 0x02, CHIP_CH343J, "CH343J", 1},
|
||||
{0x55D3, 1, 0xFF, 0x01, CHIP_CH343K, "CH343K", 1},
|
||||
{0x55D3, 1, 0xFF, 0x18, CHIP_CH343G_AUTOBAUD, "CH343G_AUTOBAUD", 1},
|
||||
{0x55D3, 1, 0xFF, 0xFF, CHIP_CH343GP, "CH343GP", 1},
|
||||
{0x55D4, 1, 0xFF, 0x09, CHIP_CH9102X, "CH9102X", 1},
|
||||
{0x55D4, 1, 0xFF, 0xFF, CHIP_CH9102F, "CH9102F", 1},
|
||||
{0x55D5, 1, 0xFF, 0xC0, CHIP_CH344L, "CH344L", 4}, // CH344L vs CH344L_V2 resolved below
|
||||
{0x55D5, 1, 0xFF, 0xFF, CHIP_CH344Q, "CH344Q", 4},
|
||||
{0x55D7, 1, 0xFF, 0xFF, CHIP_CH9103M, "CH9103M", 2},
|
||||
{0x55D8, 1, 0xFF, 0x0A, CHIP_CH9101RY, "CH9101RY", 1},
|
||||
{0x55D8, 1, 0xFF, 0xFF, CHIP_CH9101UH, "CH9101UH", 1},
|
||||
{0x55DB, 1, 0xFF, 0xFF, CHIP_CH347TF, "CH347TF", 1},
|
||||
{0x55DD, 1, 0xFF, 0xFF, CHIP_CH347TF, "CH347TF", 1},
|
||||
{0x55DA, 1, 0xFF, 0xFF, CHIP_CH347TF, "CH347TF", 2},
|
||||
{0x55DE, 1, 0xFF, 0xFF, CHIP_CH347TF, "CH347TF", 2},
|
||||
{0x55E7, 1, 0xFF, 0xFF, CHIP_CH339W, "CH339W", 1},
|
||||
{0x55DF, 1, 0xFF, 0xFF, CHIP_CH9104L, "CH9104L", 4},
|
||||
{0x55E9, 1, 0xFF, 0xFF, CHIP_CH9111L_M0, "CH9111L_M0", 1},
|
||||
{0x55EA, 1, 0xFF, 0xFF, CHIP_CH9111L_M1, "CH9111L_M1", 1},
|
||||
{0x55E8, 2, 0xFF, 0x48, CHIP_CH9114L, "CH9114L", 4},
|
||||
{0x55E8, 2, 0xFF, 0x49, CHIP_CH9114W, "CH9114W", 4},
|
||||
{0x55E8, 2, 0xFF, 0x4A, CHIP_CH9114F, "CH9114F", 4},
|
||||
{0x55EB, 4, 0x01, 0x01, CHIP_CH346C_M1, "CH346C_M1", 1},
|
||||
{0x55EB, 4, 0x01, 0xFF, CHIP_CH346C_M0, "CH346C_M0", 1},
|
||||
{0x55EC, 1, 0xFF, 0xFF, CHIP_CH346C_M2, "CH346C_M2", 2},
|
||||
};
|
||||
|
||||
void USBUartTypeCH34X::enable_channels() {
|
||||
// enable the channels
|
||||
for (auto channel : this->channels_) {
|
||||
usb_host::transfer_cb_t cb = [this](const usb_host::TransferStatus &status) {
|
||||
if (!status.success) {
|
||||
this->defer([this, error_code = status.error_code]() {
|
||||
ESP_LOGE(TAG, "CH34x chip detection failed: %s", esp_err_to_name(error_code));
|
||||
this->apply_line_settings_();
|
||||
});
|
||||
return;
|
||||
}
|
||||
CH34xChipType chiptype = CHIP_UNKNOWN;
|
||||
uint8_t num_ports = 1;
|
||||
for (const auto &e : CH34X_TABLE) {
|
||||
if (e.pid != this->pid_)
|
||||
continue;
|
||||
if (e.match != 0xFF && (status.data[e.byte_idx] & e.mask) != e.match)
|
||||
continue;
|
||||
chiptype = e.chiptype;
|
||||
num_ports = e.num_ports;
|
||||
break;
|
||||
}
|
||||
// CH344L vs CH344L_V2 requires chipver (data[0]) in addition to chiptype (data[1])
|
||||
if (chiptype == CHIP_CH344L && (status.data[0] & 0xF0) != 0x40)
|
||||
chiptype = CHIP_CH344L_V2;
|
||||
const char *name = "unknown";
|
||||
for (const auto &e : CH34X_TABLE) {
|
||||
if (e.chiptype == chiptype) {
|
||||
name = e.name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
this->defer([this, chiptype, num_ports, name]() {
|
||||
this->chiptype_ = chiptype;
|
||||
this->chip_name_ = name;
|
||||
this->num_ports_ = num_ports;
|
||||
ESP_LOGD(TAG, "CH34x chip: %s, ports: %u", name, this->num_ports_);
|
||||
this->apply_line_settings_();
|
||||
});
|
||||
};
|
||||
// Vendor-specific GET_CHIP_VERSION request (bRequest=0x5F): returns chip ID bytes
|
||||
// used to distinguish CH34x variants sharing the same PID.
|
||||
this->control_transfer(USB_VENDOR_DEV | usb_host::USB_DIR_IN, 0x5F, 0, 0, cb, {0, 0, 0, 0, 0, 0, 0, 0});
|
||||
}
|
||||
|
||||
void USBUartTypeCH34X::dump_config() {
|
||||
USBUartTypeCdcAcm::dump_config();
|
||||
ESP_LOGCONFIG(TAG, " CH34x chip: %s", this->chip_name_);
|
||||
}
|
||||
|
||||
void USBUartTypeCH34X::apply_line_settings_() {
|
||||
for (auto *channel : this->channels_) {
|
||||
if (!channel->initialised_.load())
|
||||
continue;
|
||||
usb_host::transfer_cb_t callback = [=](const usb_host::TransferStatus &status) {
|
||||
|
||||
@@ -35,6 +35,36 @@ struct CdcEps {
|
||||
uint8_t interrupt_interface_number;
|
||||
};
|
||||
|
||||
enum CH34xChipType : uint8_t {
|
||||
CHIP_CH342F = 0,
|
||||
CHIP_CH342K,
|
||||
CHIP_CH343GP,
|
||||
CHIP_CH343G_AUTOBAUD,
|
||||
CHIP_CH343K,
|
||||
CHIP_CH343J,
|
||||
CHIP_CH344L,
|
||||
CHIP_CH344L_V2,
|
||||
CHIP_CH344Q,
|
||||
CHIP_CH347TF,
|
||||
CHIP_CH9101UH,
|
||||
CHIP_CH9101RY,
|
||||
CHIP_CH9102F,
|
||||
CHIP_CH9102X,
|
||||
CHIP_CH9103M,
|
||||
CHIP_CH9104L,
|
||||
CHIP_CH340B,
|
||||
CHIP_CH339W,
|
||||
CHIP_CH9111L_M0,
|
||||
CHIP_CH9111L_M1,
|
||||
CHIP_CH9114L,
|
||||
CHIP_CH9114W,
|
||||
CHIP_CH9114F,
|
||||
CHIP_CH346C_M0,
|
||||
CHIP_CH346C_M1,
|
||||
CHIP_CH346C_M2,
|
||||
CHIP_UNKNOWN = 0xFF,
|
||||
};
|
||||
|
||||
enum UARTParityOptions {
|
||||
UART_CONFIG_PARITY_NONE = 0,
|
||||
UART_CONFIG_PARITY_ODD,
|
||||
@@ -192,10 +222,17 @@ class USBUartTypeCP210X : public USBUartTypeCdcAcm {
|
||||
class USBUartTypeCH34X : public USBUartTypeCdcAcm {
|
||||
public:
|
||||
USBUartTypeCH34X(uint16_t vid, uint16_t pid) : USBUartTypeCdcAcm(vid, pid) {}
|
||||
void dump_config() override;
|
||||
|
||||
protected:
|
||||
void enable_channels() override;
|
||||
std::vector<CdcEps> parse_descriptors(usb_device_handle_t dev_hdl) override;
|
||||
|
||||
private:
|
||||
void apply_line_settings_();
|
||||
CH34xChipType chiptype_{CHIP_UNKNOWN};
|
||||
const char *chip_name_{"unknown"};
|
||||
uint8_t num_ports_{1};
|
||||
};
|
||||
|
||||
} // namespace esphome::usb_uart
|
||||
|
||||
Reference in New Issue
Block a user