Skip to content

Commit ff81830

Browse files
committed
feat(io_expander): Allow to keep enable GPIO functions with using weak functions
1 parent 20dac6f commit ff81830

File tree

5 files changed

+234
-124
lines changed

5 files changed

+234
-124
lines changed

components/io_expander/esp_io_expander/CMakeLists.txt

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,17 @@ idf_component_register(
1111
)
1212

1313
if(CONFIG_IO_EXPANDER_ENABLE_GPIO_API_WRAPPER)
14-
target_sources(${COMPONENT_LIB} PRIVATE "esp_io_expander_gpio_wrapper.c")
14+
target_sources(${COMPONENT_LIB} PRIVATE "esp_io_expander_gpio_wrapper.c"
15+
"esp_io_expander_gpio_wrapper_weak.c")
16+
17+
target_include_directories(${COMPONENT_LIB} PRIVATE "priv_include")
1518

1619
target_link_libraries(${COMPONENT_LIB} INTERFACE "-Wl,--wrap=gpio_set_level"
1720
"-Wl,--wrap=gpio_get_level"
1821
"-Wl,--wrap=gpio_set_direction"
1922
"-Wl,--wrap=gpio_set_pull_mode"
20-
)
21-
endif()
23+
"-Wl,--wrap=gpio_config"
24+
"-Wl,--wrap=gpio_reset_pin"
25+
"-u __wrap_gpio_set_level")
26+
27+
endif()

components/io_expander/esp_io_expander/Kconfig

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ menu "ESP IO Expander"
22

33
config IO_EXPANDER_ENABLE_GPIO_API_WRAPPER
44
bool "Enable GPIO API wrapper"
5-
default n
5+
default y
66
help
7-
If enabled, ESP-IDF GPIO APIs can be used to control the IOs of IO expander.
7+
If enabled, ESP-IDF GPIO APIs can be used to control the IOs of IO expander. Full GPIO expander support links when you call esp_io_expander_gpio_wrapper_append_handler()
88

99
endmenu

components/io_expander/esp_io_expander/esp_io_expander_gpio_wrapper.c

Lines changed: 68 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,15 @@
22
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
5+
*
6+
* Linked only when the app references esp_io_expander_gpio_wrapper_append_handler
7+
* (or remove_handler). Provides strong esp_io_expander_gpio_wrapper_*.
58
*/
69

710
#include <stdlib.h>
811
#include <stdbool.h>
912
#include "esp_io_expander_gpio_wrapper.h"
13+
#include "esp_io_expander_gpio_wrapper_priv.h"
1014
#include "freertos/FreeRTOS.h"
1115
#include "freertos/portmacro.h"
1216
#include "esp_heap_caps.h"
@@ -34,13 +38,6 @@ static portMUX_TYPE s_ioexp_lock = portMUX_INITIALIZER_UNLOCKED;
3438

3539
static char *TAG = "io_expander_wrapper";
3640

37-
esp_err_t __real_gpio_set_level(gpio_num_t gpio_num, uint32_t level);
38-
int __real_gpio_get_level(gpio_num_t gpio_num);
39-
esp_err_t __real_gpio_set_direction(gpio_num_t gpio_num, gpio_mode_t mode);
40-
esp_err_t __real_gpio_set_pull_mode(gpio_num_t gpio_num, gpio_pull_mode_t pull);
41-
esp_err_t __real_gpio_config(const gpio_config_t *pGPIOConfig);
42-
esp_err_t __real_gpio_reset_pin(gpio_num_t gpio_num);
43-
4441
static bool find_ioexp_for_num(uint32_t gpio_num, esp_io_expander_handle_t *out_handler, uint32_t *out_pin_mask)
4542
{
4643
bool found = false;
@@ -65,72 +62,6 @@ static bool find_ioexp_for_num(uint32_t gpio_num, esp_io_expander_handle_t *out_
6562
return found;
6663
}
6764

68-
static esp_err_t io_expander_gpio_config(uint32_t gpio_num, const gpio_config_t *config)
69-
{
70-
if (config == NULL) {
71-
return ESP_ERR_INVALID_ARG;
72-
}
73-
esp_err_t err = ESP_OK;
74-
esp_io_expander_handle_t handle = NULL;
75-
uint32_t pin_mask = 0;
76-
if (!find_ioexp_for_num((uint32_t)gpio_num, &handle, &pin_mask)) {
77-
ESP_LOGE(TAG, "GPIO %d is not assigned to any IO Expander", gpio_num);
78-
return ESP_ERR_INVALID_ARG;
79-
}
80-
81-
if ((config->mode) & GPIO_MODE_DEF_INPUT) {
82-
err = esp_io_expander_set_dir(handle, pin_mask, IO_EXPANDER_INPUT);
83-
} else if ((config->mode) & GPIO_MODE_DEF_OUTPUT) {
84-
err = esp_io_expander_set_dir(handle, pin_mask, IO_EXPANDER_OUTPUT);
85-
if (handle->write_highz_reg) {
86-
if ((config->mode) & GPIO_MODE_DEF_OD) {
87-
err = esp_io_expander_set_output_mode(handle, pin_mask, IO_EXPANDER_OUTPUT_MODE_OPEN_DRAIN);
88-
} else {
89-
err = esp_io_expander_set_output_mode(handle, pin_mask, IO_EXPANDER_OUTPUT_MODE_PUSH_PULL);
90-
}
91-
} else if ((config->mode) & GPIO_MODE_DEF_OD) {
92-
ESP_LOGW(TAG, "IO Expander GPIO does not support open drain mode.");
93-
}
94-
} else {
95-
ESP_LOGE(TAG, "Unsupported GPIO mode %d for IO Expander GPIO %d", config->mode, gpio_num);
96-
return ESP_ERR_INVALID_ARG;
97-
}
98-
99-
esp_io_expander_pullupdown_t pull_mode = IO_EXPANDER_PULL_NONE;
100-
bool pull_mode_valid = true;
101-
if (config->pull_up_en && config->pull_down_en) {
102-
pull_mode_valid = false;
103-
} else if (config->pull_up_en) {
104-
if (!handle->write_pullup_en_reg) {
105-
pull_mode_valid = false;
106-
} else {
107-
pull_mode = IO_EXPANDER_PULL_UP;
108-
}
109-
} else if (config->pull_down_en) {
110-
if (!handle->write_pullup_en_reg || !handle->write_pullup_sel_reg) {
111-
pull_mode_valid = false;
112-
} else {
113-
pull_mode = IO_EXPANDER_PULL_DOWN;
114-
}
115-
} else {
116-
pull_mode = IO_EXPANDER_PULL_NONE;
117-
}
118-
119-
if (pull_mode_valid) {
120-
if (handle->write_pullup_en_reg) {
121-
err = esp_io_expander_set_pullupdown(handle, pin_mask, pull_mode);
122-
}
123-
} else {
124-
ESP_LOGE(TAG, "Unsupported GPIO pull mode for IO Expander GPIO %d", gpio_num);
125-
}
126-
127-
if (config->intr_type) {
128-
ESP_LOGW(TAG, "IO Expander does not support interrupts.");
129-
}
130-
131-
return err;
132-
}
133-
13465
esp_err_t esp_io_expander_gpio_wrapper_append_handler(esp_io_expander_handle_t handler, uint32_t start_io_num)
13566
{
13667
if (handler == NULL || start_io_num < GPIO_NUM_MAX) {
@@ -214,13 +145,8 @@ esp_err_t esp_io_expander_gpio_wrapper_remove_handler(esp_io_expander_handle_t h
214145
return ESP_OK;
215146
}
216147

217-
esp_err_t __wrap_gpio_set_level(gpio_num_t gpio_num, uint32_t level)
148+
esp_err_t esp_io_expander_gpio_wrapper_set_level(gpio_num_t gpio_num, uint32_t level)
218149
{
219-
if (gpio_num < GPIO_NUM_MAX) {
220-
// Call the ESP-IDF implementation for regular GPIOs
221-
return __real_gpio_set_level(gpio_num, level);
222-
}
223-
// Redirect GPIO set level calls to ESP IO Expander here
224150
esp_io_expander_handle_t handle = NULL;
225151
uint32_t pin_mask = 0;
226152
if (!find_ioexp_for_num((uint32_t)gpio_num, &handle, &pin_mask)) {
@@ -230,34 +156,24 @@ esp_err_t __wrap_gpio_set_level(gpio_num_t gpio_num, uint32_t level)
230156
return esp_io_expander_set_level(handle, pin_mask, (uint8_t)(level ? 1 : 0));
231157
}
232158

233-
int __wrap_gpio_get_level(gpio_num_t gpio_num)
159+
int esp_io_expander_gpio_wrapper_get_level(gpio_num_t gpio_num)
234160
{
235-
if (gpio_num < GPIO_NUM_MAX) {
236-
// Call the ESP-IDF implementation for regular GPIOs
237-
return __real_gpio_get_level(gpio_num);
238-
}
239-
// Redirect GPIO get level calls to ESP IO Expander here
240161
esp_io_expander_handle_t handle = NULL;
241162
uint32_t pin_mask = 0;
242163
if (!find_ioexp_for_num((uint32_t)gpio_num, &handle, &pin_mask)) {
243164
ESP_LOGE(TAG, "GPIO %d is not assigned to any IO Expander", gpio_num);
244-
return -1; // Indicate error
165+
return -1;
245166
}
246167
uint32_t level_mask = 0;
247168
esp_err_t err = esp_io_expander_get_level(handle, pin_mask, &level_mask);
248169
if (err != ESP_OK) {
249-
return -1; // Indicate error
170+
return -1;
250171
}
251172
return (level_mask & pin_mask) ? 1 : 0;
252173
}
253174

254-
esp_err_t __wrap_gpio_set_direction(gpio_num_t gpio_num, gpio_mode_t mode)
175+
esp_err_t esp_io_expander_gpio_wrapper_set_direction(gpio_num_t gpio_num, gpio_mode_t mode)
255176
{
256-
if (gpio_num < GPIO_NUM_MAX) {
257-
// Call the ESP-IDF implementation for regular GPIOs
258-
return __real_gpio_set_direction(gpio_num, mode);
259-
}
260-
// Redirect GPIO set direction calls to ESP IO Expander here
261177
esp_io_expander_handle_t handle = NULL;
262178
uint32_t pin_mask = 0;
263179
if (!find_ioexp_for_num((uint32_t)gpio_num, &handle, &pin_mask)) {
@@ -296,13 +212,8 @@ esp_err_t __wrap_gpio_set_direction(gpio_num_t gpio_num, gpio_mode_t mode)
296212
return err;
297213
}
298214

299-
esp_err_t __wrap_gpio_set_pull_mode(gpio_num_t gpio_num, gpio_pull_mode_t pull)
215+
esp_err_t esp_io_expander_gpio_wrapper_set_pull_mode(gpio_num_t gpio_num, gpio_pull_mode_t pull)
300216
{
301-
if (gpio_num < GPIO_NUM_MAX) {
302-
// Call the ESP-IDF implementation for regular GPIOs
303-
return __real_gpio_set_pull_mode(gpio_num, pull);
304-
}
305-
// Redirect GPIO set pull mode calls to ESP IO Expander here
306217
esp_io_expander_handle_t handle = NULL;
307218
uint32_t pin_mask = 0;
308219
if (!find_ioexp_for_num((uint32_t)gpio_num, &handle, &pin_mask)) {
@@ -340,37 +251,75 @@ esp_err_t __wrap_gpio_set_pull_mode(gpio_num_t gpio_num, gpio_pull_mode_t pull)
340251
return esp_io_expander_set_pullupdown(handle, pin_mask, pud);
341252
}
342253

343-
esp_err_t __wrap_gpio_config(const gpio_config_t *pGPIOConfig)
254+
esp_err_t esp_io_expander_gpio_wrapper_configure_pin(gpio_num_t gpio_num, const gpio_config_t *config)
344255
{
345-
esp_err_t ret = ESP_OK;
346-
gpio_config_t cfg = *pGPIOConfig;
347-
uint64_t invalid_mask = cfg.pin_bit_mask & ~SOC_GPIO_VALID_GPIO_MASK;
256+
if (config == NULL) {
257+
return ESP_ERR_INVALID_ARG;
258+
}
259+
esp_err_t err = ESP_OK;
260+
esp_io_expander_handle_t handle = NULL;
261+
uint32_t pin_mask = 0;
262+
if (!find_ioexp_for_num((uint32_t)gpio_num, &handle, &pin_mask)) {
263+
ESP_LOGE(TAG, "GPIO %d is not assigned to any IO Expander", gpio_num);
264+
return ESP_ERR_INVALID_ARG;
265+
}
348266

349-
// Handle invalid pins first (IO expander)
350-
while (invalid_mask) {
351-
int io_num = __builtin_ctzll(invalid_mask);
352-
invalid_mask &= invalid_mask - 1;
267+
if ((config->mode) & GPIO_MODE_DEF_INPUT) {
268+
err = esp_io_expander_set_dir(handle, pin_mask, IO_EXPANDER_INPUT);
269+
} else if ((config->mode) & GPIO_MODE_DEF_OUTPUT) {
270+
err = esp_io_expander_set_dir(handle, pin_mask, IO_EXPANDER_OUTPUT);
271+
if (handle->write_highz_reg) {
272+
if ((config->mode) & GPIO_MODE_DEF_OD) {
273+
err = esp_io_expander_set_output_mode(handle, pin_mask, IO_EXPANDER_OUTPUT_MODE_OPEN_DRAIN);
274+
} else {
275+
err = esp_io_expander_set_output_mode(handle, pin_mask, IO_EXPANDER_OUTPUT_MODE_PUSH_PULL);
276+
}
277+
} else if ((config->mode) & GPIO_MODE_DEF_OD) {
278+
ESP_LOGW(TAG, "IO Expander GPIO does not support open drain mode.");
279+
}
280+
} else {
281+
ESP_LOGE(TAG, "Unsupported GPIO mode %d for IO Expander GPIO %d", config->mode, gpio_num);
282+
return ESP_ERR_INVALID_ARG;
283+
}
284+
285+
esp_io_expander_pullupdown_t pull_mode = IO_EXPANDER_PULL_NONE;
286+
bool pull_mode_valid = true;
287+
if (config->pull_up_en && config->pull_down_en) {
288+
pull_mode_valid = false;
289+
} else if (config->pull_up_en) {
290+
if (!handle->write_pullup_en_reg) {
291+
pull_mode_valid = false;
292+
} else {
293+
pull_mode = IO_EXPANDER_PULL_UP;
294+
}
295+
} else if (config->pull_down_en) {
296+
if (!handle->write_pullup_en_reg || !handle->write_pullup_sel_reg) {
297+
pull_mode_valid = false;
298+
} else {
299+
pull_mode = IO_EXPANDER_PULL_DOWN;
300+
}
301+
} else {
302+
pull_mode = IO_EXPANDER_PULL_NONE;
303+
}
353304

354-
io_expander_gpio_config(io_num, &cfg);
355-
cfg.pin_bit_mask &= ~(1ULL << io_num);
305+
if (pull_mode_valid) {
306+
if (handle->write_pullup_en_reg) {
307+
err = esp_io_expander_set_pullupdown(handle, pin_mask, pull_mode);
308+
}
309+
} else {
310+
ESP_LOGE(TAG, "Unsupported GPIO pull mode for IO Expander GPIO %d", gpio_num);
356311
}
357312

358-
if (cfg.pin_bit_mask) {
359-
// Call the ESP-IDF implementation for regular GPIOs
360-
return __real_gpio_config(&cfg);
313+
if (config->intr_type) {
314+
ESP_LOGW(TAG, "IO Expander does not support interrupts.");
361315
}
362316

363-
return ret;
317+
return err;
364318
}
365319

366-
esp_err_t __wrap_gpio_reset_pin(gpio_num_t gpio_num)
320+
esp_err_t esp_io_expander_gpio_wrapper_reset_pin(gpio_num_t gpio_num)
367321
{
368322
esp_err_t ret = ESP_OK;
369-
if (gpio_num < GPIO_NUM_MAX) {
370-
// Call the ESP-IDF implementation for regular GPIOs
371-
return __real_gpio_reset_pin(gpio_num);
372-
}
373-
// Redirect GPIO set direction calls to ESP IO Expander here
374323
esp_io_expander_handle_t handle = NULL;
375324
uint32_t pin_mask = 0;
376325
if (!find_ioexp_for_num((uint32_t)gpio_num, &handle, &pin_mask)) {

0 commit comments

Comments
 (0)