Repository navigation
Closed
Description
Problems found in i2cdev 2.1.3 (2c88fba) that are fixed by the pull request referencing this issue. Checked against ESP-IDF v6.1 sources and on an ESP32 board (BMP180 + HD44780 via PCF8574 on one bus).
Legacy build
i2cdev_legacy.cincludes<driver/i2c.h>, butdriveris missing from the component requirements:CONFIG_I2CDEV_USE_LEGACY_DRIVER=yfails withfatal error: driver/i2c.h: No such file or directory. The requirements are evaluated on the first CMake pass, beforesdkconfigexists, so a list built fromCONFIG_I2CDEV_USE_LEGACY_DRIVERnever contains it.i2cdev_get_shared_handle()is declared ini2cdev.hfor both implementations but defined only ini2cdev.c, so the legacy build fails to link.
Bus and device lifecycle
i2cdev_init()keeps astatic bool initializedlatch thati2cdev_done()never clears. Afterdone()+init()the function returnsESP_OKwithout creating anything and every following call fails withESP_ERR_INVALID_STATE.- Teardown errors drop resources. If
i2c_del_master_bus()fails (for exampleESP_ERR_INVALID_STATEbecause devices are still attached),i2c_dev_delete_mutex()clearsbus_handleandinstalledanyway: the bus stays occupied inside the IDF driver, i2cdev loses it, and no lateri2c_new_master_bus()succeeds - the port is dead until reboot. Same for a failedi2c_master_bus_rm_device(): the handle is lost for good. i2cdev_done()deletes the port mutex even when it failed to take it, i.e. while another task holds it.- The retry loop removed the device handle on
ESP_ERR_INVALID_ARG/ESP_ERR_INVALID_STATEwithout the port mutex, without freeing the device slot and without keeping the handle when the removal failed. The trigger is wrong as well:ESP_ERR_INVALID_ARGfromi2c_master_transmit()is a caller error, not a stale handle. - A device could be on the bus without being tracked: registration lived in
i2c_dev_create_mutex(), its failure was only logged ("device will work but cleanup tracking disabled"), and a device created beforei2cdev_init()was never registered.i2cdev_done()cannot remove such devices, soi2c_del_master_bus()fails and the bus leaks. CONFIG_I2CDEV_NOLOCK=1disabled the whole bus lifecycle instead of the per-device mutex: devices were not tracked,i2c_dev_delete_mutex()did nothing,i2cdev_done()could not delete the bus.- Adding a device was racy:
dev->dev_handlewas checked before the port mutex was taken andi2c_master_bus_add_device()does not detect duplicates. Two tasks doing the first I/O on one descriptor added the device twice: the first handle leaked and the reference count became 2. - The bus was deleted when the last i2cdev device was removed, although the handle can be shared with external components through
i2cdev_get_shared_handle()(esp_lcd and similar). With an external device attachedi2c_del_master_bus()fails and leaves the port occupied; without one the external component keeps a freed handle. CONFIG_I2CDEV_MAX_DEVICES_PER_PORT(8) was a hard limit for the new driver, and exceeding it silently disabled tracking.
Argument validation
- Negative port numbers passed the
port >= I2C_NUM_MAXcheck and were used as indexes into internal arrays. sda_pin == scl_pinwas not rejected, so a zero-initialized descriptor (i2c_dev_t dev = {0}) configured GPIO0 as both SDA and SCL.sda_pullup_en/scl_pullup_enwere collapsed into a singleenable_internal_pullupflag: enabling one line enabled both.i2c_dev_check_present()returnedESP_ERR_INVALID_ARGwhen the port was not initialized, hiding the real reason.i2c_dev_read_reg()/i2c_dev_write_reg()accepted NULL buffers and zero sizes, unlike the legacy implementation of the same API.i2c_dev_read()selected the transaction fromout_dataalone, so a half-filled(out_data, out_size)pair produced a zero-length prefix transfer and four retries.malloc()/free()were used without#include <stdlib.h>.- The header documentation did not match the implementation: return codes, retry policy, lazy bus and handle creation, side effects of
check_present()/probe(), descriptor lifetime, macro requirements, cleanup order for a shared bus. Also deadCONFIG_FREERTOS_HZ/CONFIG_LOG_MAXIMUM_LEVELfallbacks and a deadclk_flagsfield presented as functional.
Metadata
Metadata
Assignees
Labels
No labels
Activity
[-]Review 2.1.3: broken legacy build, bus/device lifecycle bugs, argument validation[/-][+]Review 2.1.3: broken legacy build, bus/device lifecycle, argument validation[/+]