-
Notifications
You must be signed in to change notification settings - Fork 55
[DNM] zephyrCommon: Improved interrupt handling #136
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: next
Are you sure you want to change the base?
Conversation
3316f82 to
28b1df9
Compare
ffc4797 to
5e8e11a
Compare
5e8e11a to
a3f549f
Compare
a3f549f to
b398bfa
Compare
Zephyr's ba48d83b changes make it easy to configure pin numbering at compile, so we'll change the pin numbers to be based on the GPIO pin numbers. This is same as the traditional Arduino, using the GPIO numbers as pin numbers. Pin names written on the board, such as D1 and D2, are aliases for these pin numbers. When using multiple GPIO ports, pin numbers are numbered consecutively from the beginning. For example, if you have two 16-port GPIOs, 16 refers to the first pin (idx=0) of the second port. Signed-off-by: TOKITA Hiroshi <tokita.hiroshi@gmail.com>
Now that GPIO can be specified generically, we will discontinue the definition generation of LED_BUILTIN from builtin_led_gpios. Delete this definition where automatic generation from led0 is not an issue, and define it in variant.h in all other cases. Signed-off-by: TOKITA Hiroshi <tokita.hiroshi@gmail.com>
- The structure has been revised and unnecessary fields have been removed. - Interrupt masks are now directly modified by changing the pin_mask parameter in `struct gpio_callback`. - Renamed non-Arduino-derived `setInterruptHandler` and `handleGpioCallback` functions to Zephyr-like names. Signed-off-by: TOKITA Hiroshi <tokita.hiroshi@gmail.com>
b398bfa to
7304eac
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR refactors GPIO and interrupt handling in the Zephyr Arduino core to use a more direct port-based approach. The changes eliminate the need for the builtin-led-gpios devicetree property by using variant.h definitions or led0 aliases, simplify the interrupt callback structure by directly modifying pin_mask, and rename internal functions to follow Zephyr naming conventions.
Key changes:
- Replaced per-pin GPIO management with a port-based system using
gpio-portsdevicetree property - Streamlined interrupt handling by removing unnecessary fields from callback structure and using pin_mask directly
- Renamed internal functions to snake_case (Zephyr style):
setInterruptHandler→set_interrupt_handler,handleGpioCallback→handle_gpio_callback
Reviewed changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| variants/*/*.overlay | Added gpio-ports property and removed builtin-led-gpios property to support new port-based GPIO management |
| variants/cc3220sf_launchxl_cc3220sf/variant.h | Changed LED definitions from numeric pins to D-prefixed constants for consistency |
| variants/arduino_nano_33_ble_*/variant.h | Added LED_BUILTIN definition and pragma once header guard |
| documentation/variants.md | Updated builtin-LED documentation to reflect new variant.h precedence and removed complex builtin-led-gpios configuration examples |
| cores/arduino/Arduino.h | Refactored pin number calculation macros from DIGITAL_PIN_* to ZARD_* family, simplified LED_BUILTIN resolution logic |
| cores/arduino/zephyrCommon.cpp | Major refactoring: introduced port-based GPIO helpers, simplified interrupt callback structure, renamed internal functions to Zephyr style, updated all GPIO operations to use new helpers |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| void handleGpioCallback(const struct device *port, struct gpio_callback *cb, uint32_t pins) | ||
| { | ||
| struct gpio_port_callback *pcb = (struct gpio_port_callback *)cb; | ||
| void handle_gpio_callback(const struct device *port, struct gpio_callback *cb, uint32_t pins) { |
Copilot
AI
Dec 19, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The port parameter is declared but never used in the function body. While this matches the required GPIO callback signature, consider using ARG_UNUSED(port) or similar to suppress potential compiler warnings about unused parameters.
| void handle_gpio_callback(const struct device *port, struct gpio_callback *cb, uint32_t pins) { | |
| void handle_gpio_callback(const struct device *port, struct gpio_callback *cb, uint32_t pins) { | |
| (void)port; |
| #define GPIO_NGPIOS(n, p, i) DT_PROP(DT_GPIO_CTLR_BY_IDX(n, p, i), ngpios) | ||
| const int max_ngpios = max_in_list( | ||
| constexpr int max_ngpios = max_in_list( | ||
| 0, DT_FOREACH_PROP_ELEM_SEP(DT_PATH(zephyr_user), digital_pin_gpios, GPIO_NGPIOS, (, ))); | ||
|
|
||
| /* | ||
| * GPIO callback implementation | ||
| */ | ||
|
|
||
| struct arduino_callback { | ||
| voidFuncPtr handler; | ||
| bool enabled; | ||
| }; | ||
|
|
||
| struct gpio_port_callback { | ||
| struct gpio_callback callback; | ||
| struct arduino_callback handlers[max_ngpios]; | ||
| gpio_port_pins_t pins; | ||
| const struct device *dev; | ||
| } port_callback[port_num] = {0}; | ||
| voidFuncPtr handlers[max_ngpios]; | ||
| } port_callback[ARRAY_SIZE(gpios)]; |
Copilot
AI
Dec 19, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The handlers array is sized using max_ngpios which is calculated from digital_pin_gpios. However, the array is indexed by local_gpio_pin which operates on gpio_ports. If gpio_ports contains a port with more pins than the maximum found in digital_pin_gpios, this could cause an out-of-bounds access. Consider calculating max_ngpios from gpio_ports instead to ensure the array is always large enough for any pin on any configured GPIO port.
| void enableInterrupt(pin_size_t pinNumber) { | ||
| struct gpio_port_callback *pcb = find_gpio_port_callback(arduino_pins[pinNumber].port); | ||
| struct gpio_port_callback *pcb = find_gpio_port_callback(local_gpio_port(pinNumber)); | ||
|
|
||
| if (pcb) { | ||
| pcb->handlers[arduino_pins[pinNumber].pin].enabled = true; | ||
| pcb->callback.pin_mask |= BIT(local_gpio_pin(pinNumber)); | ||
| } |
Copilot
AI
Dec 19, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If local_gpio_pin returns pin_size_t(-1) for an invalid pin, BIT(local_gpio_pin(pinNumber)) will attempt to shift by a very large value (since -1 cast to unsigned is the maximum value), which causes undefined behavior. Consider checking if local_gpio_pin returns a valid value before using it in the BIT macro.
| void disableInterrupt(pin_size_t pinNumber) { | ||
| struct gpio_port_callback *pcb = find_gpio_port_callback(arduino_pins[pinNumber].port); | ||
| struct gpio_port_callback *pcb = find_gpio_port_callback(local_gpio_port(pinNumber)); | ||
|
|
||
| if (pcb) { | ||
| pcb->handlers[arduino_pins[pinNumber].pin].enabled = false; | ||
| pcb->callback.pin_mask &= ~BIT(local_gpio_pin(pinNumber)); | ||
| } |
Copilot
AI
Dec 19, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If local_gpio_pin returns pin_size_t(-1) for an invalid pin, BIT(local_gpio_pin(pinNumber)) will attempt to shift by a very large value (since -1 cast to unsigned is the maximum value), which causes undefined behavior. Consider checking if local_gpio_pin returns a valid value before using it in the BIT macro.
| ``` | ||
| / { | ||
| zephyr,user { | ||
| gpios = <&gpio0>, <&gpio1>; |
Copilot
AI
Dec 19, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The documentation refers to a node called "gpios" but all the overlay files use "gpio-ports" (with a hyphen). The documentation should use "gpio-ports" to match the actual property name used in the devicetree overlays.
| struct gpio_port_callback *pcb = find_gpio_port_callback(local_gpio_port(pinNumber)); | ||
|
|
Copilot
AI
Dec 19, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function does not check if local_gpio_port returns nullptr before calling find_gpio_port_callback. If an invalid pinNumber is passed, local_gpio_port could return nullptr, which would then be compared against valid device pointers in find_gpio_port_callback, potentially matching nullptr in the gpios array or causing undefined behavior. Consider adding a null check before calling find_gpio_port_callback.
| struct gpio_port_callback *pcb = find_gpio_port_callback(local_gpio_port(pinNumber)); | |
| const struct device *dev = local_gpio_port(pinNumber); | |
| if (!dev) { | |
| return; | |
| } | |
| struct gpio_port_callback *pcb = find_gpio_port_callback(dev); |
| gpio_pin_interrupt_configure(local_gpio_port(pinNumber), local_gpio_pin(pinNumber), 0); | ||
| disableInterrupt(pinNumber); |
Copilot
AI
Dec 19, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The order of operations in detachInterrupt could lead to a race condition. The GPIO interrupt is disabled (line 411) before the pin_mask is cleared via disableInterrupt (line 412). If an interrupt fires between these two operations, handle_gpio_callback could still execute because the pin_mask hasn't been cleared yet. Consider calling disableInterrupt before gpio_pin_interrupt_configure to ensure the pin_mask is cleared first, preventing the callback from executing.
| gpio_pin_interrupt_configure(local_gpio_port(pinNumber), local_gpio_pin(pinNumber), 0); | |
| disableInterrupt(pinNumber); | |
| disableInterrupt(pinNumber); | |
| gpio_pin_interrupt_configure(local_gpio_port(pinNumber), local_gpio_pin(pinNumber), 0); |
| const struct device* port = local_gpio_port(pinNumber); | ||
| const size_t pin = local_gpio_pin(pinNumber); | ||
| struct k_timer timer; | ||
| int64_t start, end, delta = 0; | ||
| const struct gpio_dt_spec *spec = &arduino_pins[pinNumber]; | ||
|
|
||
| if (!gpio_is_ready_dt(spec)) { | ||
| if (!device_is_ready(port)) { | ||
| return 0; |
Copilot
AI
Dec 19, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function does not check if local_gpio_port returns nullptr before passing it to device_is_ready. If an invalid pinNumber is provided, this could cause undefined behavior. Consider adding an explicit null check for port before using it.
| * Calculate GPIO ports/pins number statically from devicetree configuration | ||
| */ | ||
| #define DEVICE_GPIO(n, p, i) DEVICE_DT_GET(DT_PROP_BY_IDX(n, p, i)) | ||
| constexpr const struct device *gpios[] = { |
Copilot
AI
Dec 19, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The gpios array is declared constexpr but contains pointers to device structures obtained via DEVICE_DT_GET. In Zephyr, device pointers typically point to runtime-initialized structures and may not be valid in constexpr context. This could cause compilation errors depending on the Zephyr version and compiler. Consider using const instead of constexpr for the gpios array if compilation issues arise.
| constexpr const struct device *gpios[] = { | |
| const struct device *const gpios[] = { |
parameter in
struct gpio_callback.setInterruptHandlerandhandleGpioCallbackfunctions to Zephyr-like names.