diff --git a/main/tools/gpio_policy.c b/main/tools/gpio_policy.c new file mode 100644 index 0000000..5c1fb31 --- /dev/null +++ b/main/tools/gpio_policy.c @@ -0,0 +1,107 @@ +#include "tools/gpio_policy.h" + +#include "driver/gpio.h" + +#include +#include +#include + +#ifndef GPIO_IS_VALID_GPIO +#define GPIO_IS_VALID_GPIO(pin) ((pin) >= 0) +#endif + +static bool pin_in_allowlist(int pin, const char *csv) +{ + const char *cursor; + + if (!csv || csv[0] == '\0') { + return false; + } + + cursor = csv; + while (*cursor != '\0') { + char *endptr = NULL; + long value; + + while (*cursor == ' ' || *cursor == '\t' || *cursor == ',') { + cursor++; + } + if (*cursor == '\0') { + break; + } + + value = strtol(cursor, &endptr, 10); + if (endptr == cursor) { + while (*cursor != '\0' && *cursor != ',') { + cursor++; + } + continue; + } + + if ((int)value == pin) { + return true; + } + cursor = endptr; + } + + return false; +} + +static bool pin_is_allowed_impl(int pin, + const char *allowlist_csv, + int min_pin, + int max_pin, + bool block_esp32_flash_pins) +{ + bool in_policy; + + if (pin < 0) { + return false; + } + + /* Block ESP32 flash/PSRAM pins (GPIO 6-11) */ + if (block_esp32_flash_pins && pin >= 6 && pin <= 11) { + return false; + } + + if (allowlist_csv && allowlist_csv[0] != '\0') { + in_policy = pin_in_allowlist(pin, allowlist_csv); + } else { + in_policy = pin >= min_pin && pin <= max_pin; + } + + if (!in_policy) { + return false; + } + + return GPIO_IS_VALID_GPIO((gpio_num_t)pin); +} + +bool gpio_policy_pin_is_allowed(int pin) +{ +#if defined(CONFIG_IDF_TARGET_ESP32) + return pin_is_allowed_impl(pin, MIMI_GPIO_ALLOWED_CSV, + MIMI_GPIO_MIN_PIN, MIMI_GPIO_MAX_PIN, true); +#else + return pin_is_allowed_impl(pin, MIMI_GPIO_ALLOWED_CSV, + MIMI_GPIO_MIN_PIN, MIMI_GPIO_MAX_PIN, false); +#endif +} + +bool gpio_policy_pin_forbidden_hint(int pin, char *result, size_t result_len) +{ +#if defined(CONFIG_IDF_TARGET_ESP32) + if (pin >= 6 && pin <= 11) { + snprintf(result, result_len, + "Error: pin %d is reserved for ESP32 flash/PSRAM (GPIO6-11); choose a different pin", + pin); + return true; + } +#else + (void)pin; + (void)result; + (void)result_len; +#endif + + return false; +} diff --git a/main/tools/gpio_policy.h b/main/tools/gpio_policy.h new file mode 100644 index 0000000..1b70674 --- /dev/null +++ b/main/tools/gpio_policy.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include + +/* GPIO pin range defaults for ESP32-S3 (safe user-accessible pins) */ +#define MIMI_GPIO_MIN_PIN 1 +#define MIMI_GPIO_MAX_PIN 21 +#define MIMI_GPIO_ALLOWED_CSV "" /* empty = use min/max range */ + +/** + * Check if a pin is allowed for user GPIO operations. + * Validates against the allowlist or default range, and blocks + * pins reserved for flash/PSRAM on ESP32. + */ +bool gpio_policy_pin_is_allowed(int pin); + +/** + * Write a human-readable hint if the pin is forbidden for a known reason. + * Returns true if a hint was written (and the caller should return the error). + */ +bool gpio_policy_pin_forbidden_hint(int pin, char *result, size_t result_len); diff --git a/main/tools/tool_gpio.h b/main/tools/tool_gpio.h new file mode 100644 index 0000000..b435b15 --- /dev/null +++ b/main/tools/tool_gpio.h @@ -0,0 +1,27 @@ +#pragma once + +#include "esp_err.h" +#include + +/** + * Initialize GPIO tool — configure allowed pins and directions. + */ +esp_err_t tool_gpio_init(void); + +/** + * Write a GPIO pin HIGH or LOW. + * Input JSON: {"pin": , "state": <0|1>} + */ +esp_err_t tool_gpio_write_execute(const char *input_json, char *output, size_t output_size); + +/** + * Read a single GPIO pin state. + * Input JSON: {"pin": } + */ +esp_err_t tool_gpio_read_execute(const char *input_json, char *output, size_t output_size); + +/** + * Read all allowed GPIO pin states at once. + * Input JSON: {} (no parameters) + */ +esp_err_t tool_gpio_read_all_execute(const char *input_json, char *output, size_t output_size);