mirror of
https://github.com/esphome/esphome.git
synced 2025-07-29 06:36:45 +00:00
[ethernet] P4 changes and 5.3.0 deprecated warnings (#8457)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
parent
08c88ba0f2
commit
e58baab563
@ -23,8 +23,10 @@ from esphome.const import (
|
|||||||
CONF_INTERRUPT_PIN,
|
CONF_INTERRUPT_PIN,
|
||||||
CONF_MANUAL_IP,
|
CONF_MANUAL_IP,
|
||||||
CONF_MISO_PIN,
|
CONF_MISO_PIN,
|
||||||
|
CONF_MODE,
|
||||||
CONF_MOSI_PIN,
|
CONF_MOSI_PIN,
|
||||||
CONF_PAGE_ID,
|
CONF_PAGE_ID,
|
||||||
|
CONF_PIN,
|
||||||
CONF_POLLING_INTERVAL,
|
CONF_POLLING_INTERVAL,
|
||||||
CONF_RESET_PIN,
|
CONF_RESET_PIN,
|
||||||
CONF_SPI,
|
CONF_SPI,
|
||||||
@ -49,6 +51,7 @@ PHYRegister = ethernet_ns.struct("PHYRegister")
|
|||||||
CONF_PHY_ADDR = "phy_addr"
|
CONF_PHY_ADDR = "phy_addr"
|
||||||
CONF_MDC_PIN = "mdc_pin"
|
CONF_MDC_PIN = "mdc_pin"
|
||||||
CONF_MDIO_PIN = "mdio_pin"
|
CONF_MDIO_PIN = "mdio_pin"
|
||||||
|
CONF_CLK = "clk"
|
||||||
CONF_CLK_MODE = "clk_mode"
|
CONF_CLK_MODE = "clk_mode"
|
||||||
CONF_POWER_PIN = "power_pin"
|
CONF_POWER_PIN = "power_pin"
|
||||||
CONF_PHY_REGISTERS = "phy_registers"
|
CONF_PHY_REGISTERS = "phy_registers"
|
||||||
@ -73,26 +76,18 @@ SPI_ETHERNET_TYPES = ["W5500", "DM9051"]
|
|||||||
SPI_ETHERNET_DEFAULT_POLLING_INTERVAL = TimePeriodMilliseconds(milliseconds=10)
|
SPI_ETHERNET_DEFAULT_POLLING_INTERVAL = TimePeriodMilliseconds(milliseconds=10)
|
||||||
|
|
||||||
emac_rmii_clock_mode_t = cg.global_ns.enum("emac_rmii_clock_mode_t")
|
emac_rmii_clock_mode_t = cg.global_ns.enum("emac_rmii_clock_mode_t")
|
||||||
emac_rmii_clock_gpio_t = cg.global_ns.enum("emac_rmii_clock_gpio_t")
|
|
||||||
CLK_MODES = {
|
CLK_MODES = {
|
||||||
"GPIO0_IN": (
|
"CLK_EXT_IN": emac_rmii_clock_mode_t.EMAC_CLK_EXT_IN,
|
||||||
emac_rmii_clock_mode_t.EMAC_CLK_EXT_IN,
|
"CLK_OUT": emac_rmii_clock_mode_t.EMAC_CLK_OUT,
|
||||||
emac_rmii_clock_gpio_t.EMAC_CLK_IN_GPIO,
|
|
||||||
),
|
|
||||||
"GPIO0_OUT": (
|
|
||||||
emac_rmii_clock_mode_t.EMAC_CLK_OUT,
|
|
||||||
emac_rmii_clock_gpio_t.EMAC_APPL_CLK_OUT_GPIO,
|
|
||||||
),
|
|
||||||
"GPIO16_OUT": (
|
|
||||||
emac_rmii_clock_mode_t.EMAC_CLK_OUT,
|
|
||||||
emac_rmii_clock_gpio_t.EMAC_CLK_OUT_GPIO,
|
|
||||||
),
|
|
||||||
"GPIO17_OUT": (
|
|
||||||
emac_rmii_clock_mode_t.EMAC_CLK_OUT,
|
|
||||||
emac_rmii_clock_gpio_t.EMAC_CLK_OUT_180_GPIO,
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CLK_MODES_DEPRECATED = {
|
||||||
|
"GPIO0_IN": ("CLK_EXT_IN", 0),
|
||||||
|
"GPIO0_OUT": ("CLK_OUT", 0),
|
||||||
|
"GPIO16_OUT": ("CLK_OUT", 16),
|
||||||
|
"GPIO17_OUT": ("CLK_OUT", 17),
|
||||||
|
}
|
||||||
|
|
||||||
MANUAL_IP_SCHEMA = cv.Schema(
|
MANUAL_IP_SCHEMA = cv.Schema(
|
||||||
{
|
{
|
||||||
@ -154,6 +149,18 @@ def _validate(config):
|
|||||||
f"({CORE.target_framework} {CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION]}), "
|
f"({CORE.target_framework} {CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION]}), "
|
||||||
f"'{CONF_INTERRUPT_PIN}' is a required option for [ethernet]."
|
f"'{CONF_INTERRUPT_PIN}' is a required option for [ethernet]."
|
||||||
)
|
)
|
||||||
|
elif config[CONF_TYPE] != "OPENETH":
|
||||||
|
if CONF_CLK_MODE in config:
|
||||||
|
LOGGER.warning(
|
||||||
|
"[ethernet] The 'clk_mode' option is deprecated and will be removed in ESPHome 2026.1. "
|
||||||
|
"Please update your configuration to use 'clk' instead."
|
||||||
|
)
|
||||||
|
mode = CLK_MODES_DEPRECATED[config[CONF_CLK_MODE]]
|
||||||
|
config[CONF_CLK] = CLK_SCHEMA({CONF_MODE: mode[0], CONF_PIN: mode[1]})
|
||||||
|
del config[CONF_CLK_MODE]
|
||||||
|
elif CONF_CLK not in config:
|
||||||
|
raise cv.Invalid("'clk' is a required option for [ethernet].")
|
||||||
|
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
|
||||||
@ -177,14 +184,21 @@ PHY_REGISTER_SCHEMA = cv.Schema(
|
|||||||
cv.Optional(CONF_PAGE_ID): cv.hex_int,
|
cv.Optional(CONF_PAGE_ID): cv.hex_int,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
CLK_SCHEMA = cv.Schema(
|
||||||
|
{
|
||||||
|
cv.Required(CONF_MODE): cv.enum(CLK_MODES, upper=True, space="_"),
|
||||||
|
cv.Required(CONF_PIN): pins.internal_gpio_pin_number,
|
||||||
|
}
|
||||||
|
)
|
||||||
RMII_SCHEMA = BASE_SCHEMA.extend(
|
RMII_SCHEMA = BASE_SCHEMA.extend(
|
||||||
cv.Schema(
|
cv.Schema(
|
||||||
{
|
{
|
||||||
cv.Required(CONF_MDC_PIN): pins.internal_gpio_output_pin_number,
|
cv.Required(CONF_MDC_PIN): pins.internal_gpio_output_pin_number,
|
||||||
cv.Required(CONF_MDIO_PIN): pins.internal_gpio_output_pin_number,
|
cv.Required(CONF_MDIO_PIN): pins.internal_gpio_output_pin_number,
|
||||||
cv.Optional(CONF_CLK_MODE, default="GPIO0_IN"): cv.enum(
|
cv.Optional(CONF_CLK_MODE): cv.enum(
|
||||||
CLK_MODES, upper=True, space="_"
|
CLK_MODES_DEPRECATED, upper=True, space="_"
|
||||||
),
|
),
|
||||||
|
cv.Optional(CONF_CLK): CLK_SCHEMA,
|
||||||
cv.Optional(CONF_PHY_ADDR, default=0): cv.int_range(min=0, max=31),
|
cv.Optional(CONF_PHY_ADDR, default=0): cv.int_range(min=0, max=31),
|
||||||
cv.Optional(CONF_POWER_PIN): pins.internal_gpio_output_pin_number,
|
cv.Optional(CONF_POWER_PIN): pins.internal_gpio_output_pin_number,
|
||||||
cv.Optional(CONF_PHY_REGISTERS): cv.ensure_list(PHY_REGISTER_SCHEMA),
|
cv.Optional(CONF_PHY_REGISTERS): cv.ensure_list(PHY_REGISTER_SCHEMA),
|
||||||
@ -308,7 +322,8 @@ async def to_code(config):
|
|||||||
cg.add(var.set_phy_addr(config[CONF_PHY_ADDR]))
|
cg.add(var.set_phy_addr(config[CONF_PHY_ADDR]))
|
||||||
cg.add(var.set_mdc_pin(config[CONF_MDC_PIN]))
|
cg.add(var.set_mdc_pin(config[CONF_MDC_PIN]))
|
||||||
cg.add(var.set_mdio_pin(config[CONF_MDIO_PIN]))
|
cg.add(var.set_mdio_pin(config[CONF_MDIO_PIN]))
|
||||||
cg.add(var.set_clk_mode(*CLK_MODES[config[CONF_CLK_MODE]]))
|
cg.add(var.set_clk_mode(config[CONF_CLK][CONF_MODE]))
|
||||||
|
cg.add(var.set_clk_pin(config[CONF_CLK][CONF_PIN]))
|
||||||
if CONF_POWER_PIN in config:
|
if CONF_POWER_PIN in config:
|
||||||
cg.add(var.set_power_pin(config[CONF_POWER_PIN]))
|
cg.add(var.set_power_pin(config[CONF_POWER_PIN]))
|
||||||
for register_value in config.get(CONF_PHY_REGISTERS, []):
|
for register_value in config.get(CONF_PHY_REGISTERS, []):
|
||||||
|
@ -17,6 +17,22 @@
|
|||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace ethernet {
|
namespace ethernet {
|
||||||
|
|
||||||
|
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 4, 2)
|
||||||
|
// work around IDF compile issue on P4 https://github.com/espressif/esp-idf/pull/15637
|
||||||
|
#ifdef USE_ESP32_VARIANT_ESP32P4
|
||||||
|
#undef ETH_ESP32_EMAC_DEFAULT_CONFIG
|
||||||
|
#define ETH_ESP32_EMAC_DEFAULT_CONFIG() \
|
||||||
|
{ \
|
||||||
|
.smi_gpio = {.mdc_num = 31, .mdio_num = 52}, .interface = EMAC_DATA_INTERFACE_RMII, \
|
||||||
|
.clock_config = {.rmii = {.clock_mode = EMAC_CLK_EXT_IN, .clock_gpio = (emac_rmii_clock_gpio_t) 50}}, \
|
||||||
|
.dma_burst_len = ETH_DMA_BURST_LEN_32, .intr_priority = 0, \
|
||||||
|
.emac_dataif_gpio = \
|
||||||
|
{.rmii = {.tx_en_num = 49, .txd0_num = 34, .txd1_num = 35, .crs_dv_num = 28, .rxd0_num = 29, .rxd1_num = 30}}, \
|
||||||
|
.clock_config_out_in = {.rmii = {.clock_mode = EMAC_CLK_EXT_IN, .clock_gpio = (emac_rmii_clock_gpio_t) -1}}, \
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
static const char *const TAG = "ethernet";
|
static const char *const TAG = "ethernet";
|
||||||
|
|
||||||
EthernetComponent *global_eth_component; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
EthernetComponent *global_eth_component; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||||
@ -150,22 +166,18 @@ void EthernetComponent::setup() {
|
|||||||
phy_config.phy_addr = this->phy_addr_;
|
phy_config.phy_addr = this->phy_addr_;
|
||||||
phy_config.reset_gpio_num = this->power_pin_;
|
phy_config.reset_gpio_num = this->power_pin_;
|
||||||
|
|
||||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
|
||||||
eth_esp32_emac_config_t esp32_emac_config = ETH_ESP32_EMAC_DEFAULT_CONFIG();
|
eth_esp32_emac_config_t esp32_emac_config = ETH_ESP32_EMAC_DEFAULT_CONFIG();
|
||||||
|
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
|
||||||
|
esp32_emac_config.smi_gpio.mdc_num = this->mdc_pin_;
|
||||||
|
esp32_emac_config.smi_gpio.mdio_num = this->mdio_pin_;
|
||||||
|
#else
|
||||||
esp32_emac_config.smi_mdc_gpio_num = this->mdc_pin_;
|
esp32_emac_config.smi_mdc_gpio_num = this->mdc_pin_;
|
||||||
esp32_emac_config.smi_mdio_gpio_num = this->mdio_pin_;
|
esp32_emac_config.smi_mdio_gpio_num = this->mdio_pin_;
|
||||||
|
#endif
|
||||||
esp32_emac_config.clock_config.rmii.clock_mode = this->clk_mode_;
|
esp32_emac_config.clock_config.rmii.clock_mode = this->clk_mode_;
|
||||||
esp32_emac_config.clock_config.rmii.clock_gpio = this->clk_gpio_;
|
esp32_emac_config.clock_config.rmii.clock_gpio = (emac_rmii_clock_gpio_t) this->clk_pin_;
|
||||||
|
|
||||||
esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&esp32_emac_config, &mac_config);
|
esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&esp32_emac_config, &mac_config);
|
||||||
#else
|
|
||||||
mac_config.smi_mdc_gpio_num = this->mdc_pin_;
|
|
||||||
mac_config.smi_mdio_gpio_num = this->mdio_pin_;
|
|
||||||
mac_config.clock_config.rmii.clock_mode = this->clk_mode_;
|
|
||||||
mac_config.clock_config.rmii.clock_gpio = this->clk_gpio_;
|
|
||||||
|
|
||||||
esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&mac_config);
|
|
||||||
#endif
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
switch (this->type_) {
|
switch (this->type_) {
|
||||||
@ -387,10 +399,11 @@ void EthernetComponent::dump_config() {
|
|||||||
ESP_LOGCONFIG(TAG, " Power Pin: %u", this->power_pin_);
|
ESP_LOGCONFIG(TAG, " Power Pin: %u", this->power_pin_);
|
||||||
}
|
}
|
||||||
ESP_LOGCONFIG(TAG,
|
ESP_LOGCONFIG(TAG,
|
||||||
|
" CLK Pin: %u\n"
|
||||||
" MDC Pin: %u\n"
|
" MDC Pin: %u\n"
|
||||||
" MDIO Pin: %u\n"
|
" MDIO Pin: %u\n"
|
||||||
" PHY addr: %u",
|
" PHY addr: %u",
|
||||||
this->mdc_pin_, this->mdio_pin_, this->phy_addr_);
|
this->clk_pin_, this->mdc_pin_, this->mdio_pin_, this->phy_addr_);
|
||||||
#endif
|
#endif
|
||||||
ESP_LOGCONFIG(TAG, " Type: %s", eth_type);
|
ESP_LOGCONFIG(TAG, " Type: %s", eth_type);
|
||||||
}
|
}
|
||||||
@ -611,10 +624,8 @@ void EthernetComponent::set_phy_addr(uint8_t phy_addr) { this->phy_addr_ = phy_a
|
|||||||
void EthernetComponent::set_power_pin(int power_pin) { this->power_pin_ = power_pin; }
|
void EthernetComponent::set_power_pin(int power_pin) { this->power_pin_ = power_pin; }
|
||||||
void EthernetComponent::set_mdc_pin(uint8_t mdc_pin) { this->mdc_pin_ = mdc_pin; }
|
void EthernetComponent::set_mdc_pin(uint8_t mdc_pin) { this->mdc_pin_ = mdc_pin; }
|
||||||
void EthernetComponent::set_mdio_pin(uint8_t mdio_pin) { this->mdio_pin_ = mdio_pin; }
|
void EthernetComponent::set_mdio_pin(uint8_t mdio_pin) { this->mdio_pin_ = mdio_pin; }
|
||||||
void EthernetComponent::set_clk_mode(emac_rmii_clock_mode_t clk_mode, emac_rmii_clock_gpio_t clk_gpio) {
|
void EthernetComponent::set_clk_pin(uint8_t clk_pin) { this->clk_pin_ = clk_pin; }
|
||||||
this->clk_mode_ = clk_mode;
|
void EthernetComponent::set_clk_mode(emac_rmii_clock_mode_t clk_mode) { this->clk_mode_ = clk_mode; }
|
||||||
this->clk_gpio_ = clk_gpio;
|
|
||||||
}
|
|
||||||
void EthernetComponent::add_phy_register(PHYRegister register_value) { this->phy_registers_.push_back(register_value); }
|
void EthernetComponent::add_phy_register(PHYRegister register_value) { this->phy_registers_.push_back(register_value); }
|
||||||
#endif
|
#endif
|
||||||
void EthernetComponent::set_type(EthernetType type) { this->type_ = type; }
|
void EthernetComponent::set_type(EthernetType type) { this->type_ = type; }
|
||||||
|
@ -76,7 +76,8 @@ class EthernetComponent : public Component {
|
|||||||
void set_power_pin(int power_pin);
|
void set_power_pin(int power_pin);
|
||||||
void set_mdc_pin(uint8_t mdc_pin);
|
void set_mdc_pin(uint8_t mdc_pin);
|
||||||
void set_mdio_pin(uint8_t mdio_pin);
|
void set_mdio_pin(uint8_t mdio_pin);
|
||||||
void set_clk_mode(emac_rmii_clock_mode_t clk_mode, emac_rmii_clock_gpio_t clk_gpio);
|
void set_clk_pin(uint8_t clk_pin);
|
||||||
|
void set_clk_mode(emac_rmii_clock_mode_t clk_mode);
|
||||||
void add_phy_register(PHYRegister register_value);
|
void add_phy_register(PHYRegister register_value);
|
||||||
#endif
|
#endif
|
||||||
void set_type(EthernetType type);
|
void set_type(EthernetType type);
|
||||||
@ -123,10 +124,10 @@ class EthernetComponent : public Component {
|
|||||||
// Group all 32-bit members first
|
// Group all 32-bit members first
|
||||||
int power_pin_{-1};
|
int power_pin_{-1};
|
||||||
emac_rmii_clock_mode_t clk_mode_{EMAC_CLK_EXT_IN};
|
emac_rmii_clock_mode_t clk_mode_{EMAC_CLK_EXT_IN};
|
||||||
emac_rmii_clock_gpio_t clk_gpio_{EMAC_CLK_IN_GPIO};
|
|
||||||
std::vector<PHYRegister> phy_registers_{};
|
std::vector<PHYRegister> phy_registers_{};
|
||||||
|
|
||||||
// Group all 8-bit members together
|
// Group all 8-bit members together
|
||||||
|
uint8_t clk_pin_{0};
|
||||||
uint8_t phy_addr_{0};
|
uint8_t phy_addr_{0};
|
||||||
uint8_t mdc_pin_{23};
|
uint8_t mdc_pin_{23};
|
||||||
uint8_t mdio_pin_{18};
|
uint8_t mdio_pin_{18};
|
||||||
|
@ -2,7 +2,9 @@ ethernet:
|
|||||||
type: DP83848
|
type: DP83848
|
||||||
mdc_pin: 23
|
mdc_pin: 23
|
||||||
mdio_pin: 25
|
mdio_pin: 25
|
||||||
clk_mode: GPIO0_IN
|
clk:
|
||||||
|
pin: 0
|
||||||
|
mode: CLK_EXT_IN
|
||||||
phy_addr: 0
|
phy_addr: 0
|
||||||
power_pin: 26
|
power_pin: 26
|
||||||
manual_ip:
|
manual_ip:
|
||||||
|
@ -2,7 +2,9 @@ ethernet:
|
|||||||
type: IP101
|
type: IP101
|
||||||
mdc_pin: 23
|
mdc_pin: 23
|
||||||
mdio_pin: 25
|
mdio_pin: 25
|
||||||
clk_mode: GPIO0_IN
|
clk:
|
||||||
|
pin: 0
|
||||||
|
mode: CLK_EXT_IN
|
||||||
phy_addr: 0
|
phy_addr: 0
|
||||||
power_pin: 26
|
power_pin: 26
|
||||||
manual_ip:
|
manual_ip:
|
||||||
|
@ -2,7 +2,9 @@ ethernet:
|
|||||||
type: JL1101
|
type: JL1101
|
||||||
mdc_pin: 23
|
mdc_pin: 23
|
||||||
mdio_pin: 25
|
mdio_pin: 25
|
||||||
clk_mode: GPIO0_IN
|
clk:
|
||||||
|
pin: 0
|
||||||
|
mode: CLK_EXT_IN
|
||||||
phy_addr: 0
|
phy_addr: 0
|
||||||
power_pin: 26
|
power_pin: 26
|
||||||
manual_ip:
|
manual_ip:
|
||||||
|
@ -2,7 +2,9 @@ ethernet:
|
|||||||
type: KSZ8081
|
type: KSZ8081
|
||||||
mdc_pin: 23
|
mdc_pin: 23
|
||||||
mdio_pin: 25
|
mdio_pin: 25
|
||||||
clk_mode: GPIO0_IN
|
clk:
|
||||||
|
pin: 0
|
||||||
|
mode: CLK_EXT_IN
|
||||||
phy_addr: 0
|
phy_addr: 0
|
||||||
power_pin: 26
|
power_pin: 26
|
||||||
manual_ip:
|
manual_ip:
|
||||||
|
@ -2,7 +2,9 @@ ethernet:
|
|||||||
type: KSZ8081RNA
|
type: KSZ8081RNA
|
||||||
mdc_pin: 23
|
mdc_pin: 23
|
||||||
mdio_pin: 25
|
mdio_pin: 25
|
||||||
clk_mode: GPIO0_IN
|
clk:
|
||||||
|
pin: 0
|
||||||
|
mode: CLK_EXT_IN
|
||||||
phy_addr: 0
|
phy_addr: 0
|
||||||
power_pin: 26
|
power_pin: 26
|
||||||
manual_ip:
|
manual_ip:
|
||||||
|
@ -2,7 +2,9 @@ ethernet:
|
|||||||
type: LAN8720
|
type: LAN8720
|
||||||
mdc_pin: 23
|
mdc_pin: 23
|
||||||
mdio_pin: 25
|
mdio_pin: 25
|
||||||
clk_mode: GPIO0_IN
|
clk:
|
||||||
|
pin: 0
|
||||||
|
mode: CLK_EXT_IN
|
||||||
phy_addr: 0
|
phy_addr: 0
|
||||||
power_pin: 26
|
power_pin: 26
|
||||||
manual_ip:
|
manual_ip:
|
||||||
|
@ -2,7 +2,9 @@ ethernet:
|
|||||||
type: RTL8201
|
type: RTL8201
|
||||||
mdc_pin: 23
|
mdc_pin: 23
|
||||||
mdio_pin: 25
|
mdio_pin: 25
|
||||||
clk_mode: GPIO0_IN
|
clk:
|
||||||
|
pin: 0
|
||||||
|
mode: CLK_EXT_IN
|
||||||
phy_addr: 0
|
phy_addr: 0
|
||||||
power_pin: 26
|
power_pin: 26
|
||||||
manual_ip:
|
manual_ip:
|
||||||
|
@ -2,7 +2,9 @@ ethernet:
|
|||||||
type: LAN8720
|
type: LAN8720
|
||||||
mdc_pin: 23
|
mdc_pin: 23
|
||||||
mdio_pin: 25
|
mdio_pin: 25
|
||||||
clk_mode: GPIO0_IN
|
clk:
|
||||||
|
pin: 0
|
||||||
|
mode: CLK_EXT_IN
|
||||||
phy_addr: 0
|
phy_addr: 0
|
||||||
power_pin: 26
|
power_pin: 26
|
||||||
manual_ip:
|
manual_ip:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user