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"
1317#include "esp_err.h"
18+ #include "esp_check.h"
1419#include "esp_log.h"
1520#include "driver/gpio.h"
1621#include "soc/gpio_num.h"
@@ -33,11 +38,6 @@ static portMUX_TYPE s_ioexp_lock = portMUX_INITIALIZER_UNLOCKED;
3338
3439static char * TAG = "io_expander_wrapper" ;
3540
36- esp_err_t __real_gpio_set_level (gpio_num_t gpio_num , uint32_t level );
37- int __real_gpio_get_level (gpio_num_t gpio_num );
38- esp_err_t __real_gpio_set_direction (gpio_num_t gpio_num , gpio_mode_t mode );
39- esp_err_t __real_gpio_set_pull_mode (gpio_num_t gpio_num , gpio_pull_mode_t pull );
40-
4141static bool find_ioexp_for_num (uint32_t gpio_num , esp_io_expander_handle_t * out_handler , uint32_t * out_pin_mask )
4242{
4343 bool found = false;
@@ -145,53 +145,38 @@ esp_err_t esp_io_expander_gpio_wrapper_remove_handler(esp_io_expander_handle_t h
145145 return ESP_OK ;
146146}
147147
148- 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 )
149149{
150- if (gpio_num < GPIO_NUM_MAX ) {
151- // Call the ESP-IDF implementation for regular GPIOs
152- return __real_gpio_set_level (gpio_num , level );
153- }
154- // Redirect GPIO set level calls to ESP IO Expander here
155- esp_io_expander_handle_t handler = NULL ;
150+ esp_io_expander_handle_t handle = NULL ;
156151 uint32_t pin_mask = 0 ;
157- if (!find_ioexp_for_num ((uint32_t )gpio_num , & handler , & pin_mask )) {
152+ if (!find_ioexp_for_num ((uint32_t )gpio_num , & handle , & pin_mask )) {
158153 ESP_LOGE (TAG , "GPIO %d is not assigned to any IO Expander" , gpio_num );
159154 return ESP_ERR_INVALID_ARG ;
160155 }
161- return esp_io_expander_set_level (handler , pin_mask , (uint8_t )(level ? 1 : 0 ));
156+ return esp_io_expander_set_level (handle , pin_mask , (uint8_t )(level ? 1 : 0 ));
162157}
163158
164- int __wrap_gpio_get_level (gpio_num_t gpio_num )
159+ int esp_io_expander_gpio_wrapper_get_level (gpio_num_t gpio_num )
165160{
166- if (gpio_num < GPIO_NUM_MAX ) {
167- // Call the ESP-IDF implementation for regular GPIOs
168- return __real_gpio_get_level (gpio_num );
169- }
170- // Redirect GPIO get level calls to ESP IO Expander here
171- esp_io_expander_handle_t handler = NULL ;
161+ esp_io_expander_handle_t handle = NULL ;
172162 uint32_t pin_mask = 0 ;
173- if (!find_ioexp_for_num ((uint32_t )gpio_num , & handler , & pin_mask )) {
163+ if (!find_ioexp_for_num ((uint32_t )gpio_num , & handle , & pin_mask )) {
174164 ESP_LOGE (TAG , "GPIO %d is not assigned to any IO Expander" , gpio_num );
175- return -1 ; // Indicate error
165+ return -1 ;
176166 }
177167 uint32_t level_mask = 0 ;
178- esp_err_t err = esp_io_expander_get_level (handler , pin_mask , & level_mask );
168+ esp_err_t err = esp_io_expander_get_level (handle , pin_mask , & level_mask );
179169 if (err != ESP_OK ) {
180- return -1 ; // Indicate error
170+ return -1 ;
181171 }
182172 return (level_mask & pin_mask ) ? 1 : 0 ;
183173}
184174
185- 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 )
186176{
187- if (gpio_num < GPIO_NUM_MAX ) {
188- // Call the ESP-IDF implementation for regular GPIOs
189- return __real_gpio_set_direction (gpio_num , mode );
190- }
191- // Redirect GPIO set direction calls to ESP IO Expander here
192- esp_io_expander_handle_t handler = NULL ;
177+ esp_io_expander_handle_t handle = NULL ;
193178 uint32_t pin_mask = 0 ;
194- if (!find_ioexp_for_num ((uint32_t )gpio_num , & handler , & pin_mask )) {
179+ if (!find_ioexp_for_num ((uint32_t )gpio_num , & handle , & pin_mask )) {
195180 ESP_LOGE (TAG , "GPIO %d is not assigned to any IO Expander" , gpio_num );
196181 return ESP_ERR_INVALID_ARG ;
197182 }
@@ -209,7 +194,7 @@ esp_err_t __wrap_gpio_set_direction(gpio_num_t gpio_num, gpio_mode_t mode)
209194 case GPIO_MODE_OUTPUT_OD :
210195 dir = IO_EXPANDER_OUTPUT ;
211196 out_mode = IO_EXPANDER_OUTPUT_MODE_OPEN_DRAIN ;
212- if (!handler -> write_highz_reg ) {
197+ if (!handle -> write_highz_reg ) {
213198 mode_valid = false;
214199 }
215200 break ;
@@ -220,38 +205,33 @@ esp_err_t __wrap_gpio_set_direction(gpio_num_t gpio_num, gpio_mode_t mode)
220205 ESP_LOGE (TAG , "Unsupported GPIO mode %d for IO Expander GPIO %d" , mode , gpio_num );
221206 return ESP_ERR_INVALID_ARG ;
222207 }
223- esp_err_t err = esp_io_expander_set_dir (handler , pin_mask , dir );
224- if (err == ESP_OK && dir == IO_EXPANDER_OUTPUT && handler -> write_highz_reg ) {
225- err = esp_io_expander_set_output_mode (handler , pin_mask , out_mode );
208+ esp_err_t err = esp_io_expander_set_dir (handle , pin_mask , dir );
209+ if (err == ESP_OK && dir == IO_EXPANDER_OUTPUT && handle -> write_highz_reg ) {
210+ err = esp_io_expander_set_output_mode (handle , pin_mask , out_mode );
226211 }
227212 return err ;
228213}
229214
230- 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 )
231216{
232- if (gpio_num < GPIO_NUM_MAX ) {
233- // Call the ESP-IDF implementation for regular GPIOs
234- return __real_gpio_set_pull_mode (gpio_num , pull );
235- }
236- // Redirect GPIO set pull mode calls to ESP IO Expander here
237- esp_io_expander_handle_t handler = NULL ;
217+ esp_io_expander_handle_t handle = NULL ;
238218 uint32_t pin_mask = 0 ;
239- if (!find_ioexp_for_num ((uint32_t )gpio_num , & handler , & pin_mask )) {
219+ if (!find_ioexp_for_num ((uint32_t )gpio_num , & handle , & pin_mask )) {
240220 ESP_LOGE (TAG , "GPIO %d is not assigned to any IO Expander" , gpio_num );
241221 return ESP_ERR_INVALID_ARG ;
242222 }
243223 esp_io_expander_pullupdown_t pud = IO_EXPANDER_PULL_NONE ;
244224 bool pud_valid = true;
245225 switch (pull ) {
246226 case GPIO_PULLUP_ONLY :
247- if (!handler -> write_pullup_en_reg ) {
227+ if (!handle -> write_pullup_en_reg ) {
248228 pud_valid = false;
249229 } else {
250230 pud = IO_EXPANDER_PULL_UP ;
251231 }
252232 break ;
253233 case GPIO_PULLDOWN_ONLY :
254- if (!handler -> write_pullup_en_reg || !handler -> write_pullup_sel_reg ) {
234+ if (!handle -> write_pullup_en_reg || !handle -> write_pullup_sel_reg ) {
255235 pud_valid = false;
256236 } else {
257237 pud = IO_EXPANDER_PULL_DOWN ;
@@ -268,5 +248,98 @@ esp_err_t __wrap_gpio_set_pull_mode(gpio_num_t gpio_num, gpio_pull_mode_t pull)
268248 ESP_LOGE (TAG , "Unsupported GPIO pull mode %d for IO Expander GPIO %d" , pull , gpio_num );
269249 return ESP_ERR_INVALID_ARG ;
270250 }
271- return esp_io_expander_set_pullupdown (handler , pin_mask , pud );
251+ return esp_io_expander_set_pullupdown (handle , pin_mask , pud );
252+ }
253+
254+ esp_err_t esp_io_expander_gpio_wrapper_configure_pin (gpio_num_t gpio_num , const gpio_config_t * config )
255+ {
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+ }
266+
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+ }
304+
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 );
311+ }
312+
313+ if (config -> intr_type ) {
314+ ESP_LOGW (TAG , "IO Expander does not support interrupts." );
315+ }
316+
317+ return err ;
318+ }
319+
320+ esp_err_t esp_io_expander_gpio_wrapper_reset_pin (gpio_num_t gpio_num )
321+ {
322+ esp_err_t ret = ESP_OK ;
323+ esp_io_expander_handle_t handle = NULL ;
324+ uint32_t pin_mask = 0 ;
325+ if (!find_ioexp_for_num ((uint32_t )gpio_num , & handle , & pin_mask )) {
326+ ESP_LOGE (TAG , "GPIO %d is not assigned to any IO Expander" , gpio_num );
327+ return ESP_ERR_INVALID_ARG ;
328+ }
329+
330+ // Set level to LOW
331+ ESP_RETURN_ON_ERROR (esp_io_expander_set_level (handle , pin_mask , (uint8_t )(0 )), TAG ,
332+ "Set level of IO expander pin %d failed." , gpio_num );
333+
334+ // Set as input
335+ ESP_RETURN_ON_ERROR (esp_io_expander_set_dir (handle , pin_mask , IO_EXPANDER_INPUT ), TAG ,
336+ "Set direction of IO expander pin %d failed." , gpio_num );
337+
338+ // Remove pull resistors
339+ if (handle -> write_pullup_en_reg ) {
340+ ESP_RETURN_ON_ERROR (esp_io_expander_set_pullupdown (handle , pin_mask , IO_EXPANDER_PULL_NONE ), TAG ,
341+ "Set pull mode of IO expander pin %d failed." , gpio_num );
342+ }
343+
344+ return ret ;
272345}
0 commit comments