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
3539static 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-
4441static 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-
13465esp_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