drivers: health: Add MAXM86161 Optical PPG AFE Driver Support - #3270
drivers: health: Add MAXM86161 Optical PPG AFE Driver Support#3270jcroleda wants to merge 4 commits into
Conversation
There was a problem hiding this comment.
Initial review in this PR #3267 already addressed Thank You!
Added some more comment, also the Maxim Build on the CI fails. Please also check that.
| target_include_directories(maxm86161 PUBLIC | ||
| $ {CMAKE_CURRENT_SOURCE_DIR}/src/examples/basic | ||
| ) | ||
| endif() |
There was a problem hiding this comment.
I think there are unnecessary tabs here as well as the other if below
| * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||
| * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | ||
| * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, | ||
| * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
There was a problem hiding this comment.
Following licensing changes, license should be this now
| * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| ******************************************************************************** | |
| * Copyright 2026(c) Analog Devices, Inc. | |
| * | |
| * SPDX-License-Identifier: BSD-3-Clause | |
| *******************************************************************************/ |
| * -EINVAL if arguments are invalid or IRQ is not enabled, | ||
| * -ENOMEM if descriptor allocation fails, or a propagated error | ||
| * from the driver/FIFO/IRQ setup. | ||
| */ |
There was a problem hiding this comment.
doxygen comments are usually in the source file
573295d to
f809daa
Compare
|
Changelog V2:
|
rbolboac
left a comment
There was a problem hiding this comment.
took a look at the driver for now, nothing too serious, mainly check the file again for all instances in which you use magic numbers and use macros instead
| { | ||
| int ret; | ||
|
|
||
| if (!dev || ppg_sr > 0x13) |
There was a problem hiding this comment.
use macro instead of magic numbers
| if (ret) | ||
| return ret; | ||
|
|
||
| *overflow_count = reg_val & NO_OS_GENMASK(6, 0); |
There was a problem hiding this comment.
use a mask macro instead of magic number
| } | ||
|
|
||
| if (dev->gpio_intb) { | ||
| r = no_os_gpio_remove(dev->gpio_intb); |
There was a problem hiding this comment.
why is this r used instead of ret?
RaduSabau1
left a comment
There was a problem hiding this comment.
some comments from my side
| if (dev->gpio_intb) | ||
| no_os_gpio_remove(dev->gpio_intb); | ||
| err_bus: | ||
| ret = no_os_i2c_remove(dev->i2c_desc); |
There was a problem hiding this comment.
assigning no_os_i2c_remove return value to ret would overwrite the error code stored through ret at this point and thus report a failure with (most probably, unless i2c_remove fails) a success code.
| if (ret) | ||
| goto err_gpio; | ||
|
|
||
| ret = no_os_irq_register_callback(dev->irq_ctrl, dev->gpio_intb->number, |
There was a problem hiding this comment.
this code path (irq_init) should also check for gpio_intb, otherwise dev->gpio_intb->number is dereferenced on a NULL gpio_intb reuslting in a NULL-pointer crash.
| if (!dev || led_num == 0 || led_num > MAXM86161_LED_NUM_PILOT_GREEN) | ||
| return -EINVAL; | ||
|
|
||
| reg_addr = MAXM86161_REG_LED1_PA + (led_num - 1); |
There was a problem hiding this comment.
if led_num is 4 (PILOT_GREEN) the computed register address is 0x26 which seems like a reserved address instead.
| * @return 0 on success, negative error code otherwise. | ||
| */ | ||
| int maxm86161_burst_reg_read(struct maxm86161_dev *dev, uint8_t reg_addr, | ||
| uint8_t *reg_data, uint16_t count) |
There was a problem hiding this comment.
have you tested this with a near-full FIFO read? the reads are chunked which is correct. however I see the destination pointer between chunks never advances so a near-full FIFO read could corrupt the buffer. please check on this.
| buf[0] = reg_addr; | ||
| memcpy(&buf[1], reg_data, count); | ||
|
|
||
| ret = no_os_i2c_write(dev->i2c_desc, buf, count + 1, 1); |
There was a problem hiding this comment.
the truncation of count is present here too, although no current call site exposes this.
| if (ret) | ||
| goto remove_uart; | ||
|
|
||
| no_os_uart_remove(uart_desc); |
There was a problem hiding this comment.
every successful run seems to free this uart_desc twice. no_os_uart_remove indeed checks for dev so an actual double-free wouldn't occur though the code itself looks wrong this way, this line could perhaps be removed and the if (ret) goto remove_uart; nevertheless since they become redundant in this case.
| goto cleanup; | ||
| } | ||
|
|
||
| ret = maxm86161_set_led_cfg(dev, MAXM86161_LED_SRC_PILOT_GREEN, |
There was a problem hiding this comment.
this one seems guaranteed to return -EINVAL. LED_SRC_PILOT_GREEN(8) is later used as led_num when calling set_led_range which checks for led_num > LED_NUM_RED(3) and returns -EINVAL if the condition (which is) is met.
| #define MAXM86161_SELFTEST 1 | ||
|
|
||
| #if MAXM86161_SELFTEST | ||
| static void maxm86161_selftest(struct maxm86161_iio_desc *desc) |
There was a problem hiding this comment.
where is this called in example_main()?
There was a problem hiding this comment.
This was intended mainly for debugging purposes with the GPIO IRQ during development. I will remove this from the example.
| case MAXM86161_IIO_LP_MODE: | ||
| return maxm86161_set_low_power_mode(dev, (bool)val); | ||
|
|
||
| case MAXM86161_IIO_BURST_EN: |
There was a problem hiding this comment.
seems like writing burst_en forcefully rewrites the rate to 8Hz and modifying the rate also forcefully enables the burst. a way of avoiding this (and making the two attributes independent) would be to have separate helper function for each e.g. set_burst_mode getting split in set_burst_enable and set_burst_rate or something simillar.
| goto err_remove_mutex; | ||
| } | ||
|
|
||
| return 0; |
There was a problem hiding this comment.
not only that this is line is duplicated, but *iio_desc =desc; which has a check itself is happening after the iio_desc assignment
f809daa to
66452fe
Compare
66452fe to
64b9e60
Compare
The MAXM86161 is an ultra-low-power, integrated optical data acquisition device. The MAXM86161 supports 3 programmable high-current LED drivers and an analog front-end with a 19-bit ADC, Ambient-Light Cancellation, and a picket-fence detect and replace algorithm. This device is used for optical sensing applications such as heart rate detection and pulse oximetry. This commit adds no-OS driver support for the MAXM86161 by exposing device attributes and data. Assisted-by: Claude Code - Opus 4.8 (1M Context) Signed-off-by: Jan Carlo Roleda <jancarlo.roleda@analog.com>
Adds MAXM86161 Sphinx Documentation, and adds a new Health subsystem. Assisted-by: Claude Code - Opus 4.8 (1M Context) Signed-off-by: Jan Carlo Roleda <jancarlo.roleda@analog.com>
Adds sample application for MAXM86161 to sample driver API and device connection. - Basic Non-streaming example using base driver APIs - IIO example initializing a device server instance - Attribute test app to validate Driver access to device attributes Assisted-by: Claude Code - Opus 4.8 (1M Context) Signed-off-by: Jan Carlo Roleda <jancarlo.roleda@analog.com>
Adds project documentation for MAXM86161 project applications to build and flash drivers for the MAX32655FTHR board, and connecting IIO on receiver to access device attributes and buffers. Assisted-by: Claude Code - Opus 4.8 (1M Context) Signed-off-by: Jan Carlo Roleda <jancarlo.roleda@analog.com>
64b9e60 to
a63bd07
Compare
|
Changelog V3:
|
Pull Request Description
The MAXM86161 is an ultra-low-power, integrated optical data acquisition device. The MAXM86161 supports 3 programmable high-current LED drivers and an analog front-end with a 19-bit ADC, Ambient-Light Cancellation, and a picket-fence detect and replace algorithm. This device is used for optical sensing applications such as heart rate detection and pulse oximetry.
This PR adds driver support for the MAXM86161 and adds example projects that show example application uses for this device.
Datasheet: MAXM86161
PR Type
PR Checklist