11bcc5e0c3
- Add static memory pool implementation (se050_mem_pool.c/h) - Replace all malloc/calloc with pool allocations - Replace all free with pool deallocations - Remove strdup usage (use fixed-size buffer instead) - Update I2C HAL to use fixed-size dev_path array - All 24 tests pass with static memory only Suitable for embedded environments (u-boot, ESP32) without heap.
240 lines
4.9 KiB
C
240 lines
4.9 KiB
C
/**
|
|
* @file se050_i2c_hal.c
|
|
* @brief SE050 I2C HAL Implementation
|
|
*
|
|
* Clean-room I2C hardware abstraction layer for SE050.
|
|
*
|
|
* License: MIT (Clean-room implementation)
|
|
*/
|
|
|
|
#define _POSIX_C_SOURCE 200809L
|
|
#include "se050_i2c_hal.h"
|
|
#include "se050_wireguard.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <sys/ioctl.h>
|
|
#include <linux/i2c-dev.h>
|
|
#include <time.h>
|
|
|
|
#ifdef __linux__
|
|
/* Linux-specific I2C IOCTL definitions */
|
|
#ifndef I2C_SLAVE
|
|
#define I2C_SLAVE 0x0703
|
|
#endif
|
|
#ifndef I2C_SMBUS
|
|
#define I2C_SMBUS 0x0720
|
|
#endif
|
|
#endif
|
|
|
|
/* SE050 default I2C address (7-bit) */
|
|
#define SE050_I2C_ADDR_DEFAULT 0x48
|
|
|
|
/* I2C timeout in milliseconds */
|
|
#define SE050_I2C_TIMEOUT_MS 1000
|
|
|
|
/* Maximum retry count for I2C operations */
|
|
#define SE050_I2C_MAX_RETRY 3
|
|
|
|
/* SE050 wakeup delay in milliseconds */
|
|
#define SE050_WAKEUP_DELAY_MS 5
|
|
|
|
/**
|
|
* @brief Sleep for specified milliseconds
|
|
*/
|
|
static void se050_sleep_ms(unsigned int ms)
|
|
{
|
|
#ifdef __linux__
|
|
struct timespec ts;
|
|
ts.tv_sec = ms / 1000;
|
|
ts.tv_nsec = (ms % 1000) * 1000000L;
|
|
nanosleep(&ts, NULL);
|
|
#else
|
|
/* Fallback for non-Linux platforms */
|
|
volatile int i;
|
|
for (i = 0; i < ms * 1000; i++);
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* @brief Open I2C device
|
|
*/
|
|
static void *i2c_open(const char *dev_path)
|
|
{
|
|
int fd;
|
|
|
|
if (!dev_path) {
|
|
return NULL;
|
|
}
|
|
|
|
fd = open(dev_path, O_RDWR);
|
|
if (fd < 0) {
|
|
perror("I2C open failed");
|
|
return NULL;
|
|
}
|
|
|
|
return (void *)(intptr_t)fd;
|
|
}
|
|
|
|
/**
|
|
* @brief Close I2C device
|
|
*/
|
|
static void i2c_close(void *handle)
|
|
{
|
|
int fd = (int)(intptr_t)handle;
|
|
if (fd >= 0) {
|
|
close(fd);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Set I2C slave address
|
|
*/
|
|
static se050_status_t i2c_set_slave_addr(void *handle, uint8_t addr)
|
|
{
|
|
int fd = (int)(intptr_t)handle;
|
|
int ret;
|
|
|
|
/* addr is already 7-bit address */
|
|
ret = ioctl(fd, I2C_SLAVE, addr);
|
|
if (ret < 0) {
|
|
perror("I2C set slave address failed");
|
|
return SE050_ERR_I2C;
|
|
}
|
|
|
|
return SE050_OK;
|
|
}
|
|
|
|
/* ============================================================================
|
|
* Public API Implementation
|
|
* ============================================================================ */
|
|
|
|
se050_status_t se050_i2c_init(se050_i2c_hal_t *hal, const char *dev_path, uint8_t slave_addr)
|
|
{
|
|
void *handle;
|
|
|
|
if (!hal || !dev_path) {
|
|
return SE050_ERR_INVALID_ARG;
|
|
}
|
|
|
|
memset(hal, 0, sizeof(*hal));
|
|
|
|
/* Open I2C device */
|
|
handle = i2c_open(dev_path);
|
|
if (!handle) {
|
|
return SE050_ERR_I2C;
|
|
}
|
|
|
|
/* Set slave address */
|
|
if (i2c_set_slave_addr(handle, slave_addr) != SE050_OK) {
|
|
i2c_close(handle);
|
|
return SE050_ERR_I2C;
|
|
}
|
|
|
|
hal->handle = handle;
|
|
hal->slave_addr = slave_addr;
|
|
strncpy(hal->dev_path, dev_path, SE050_I2C_DEV_PATH_MAX - 1);
|
|
hal->dev_path[SE050_I2C_DEV_PATH_MAX - 1] = '\0';
|
|
|
|
return SE050_OK;
|
|
}
|
|
|
|
void se050_i2c_close(se050_i2c_hal_t *hal)
|
|
{
|
|
if (!hal) {
|
|
return;
|
|
}
|
|
|
|
if (hal->handle) {
|
|
i2c_close(hal->handle);
|
|
hal->handle = NULL;
|
|
}
|
|
|
|
hal->dev_path[0] = '\0';
|
|
hal->slave_addr = 0;
|
|
}
|
|
|
|
int se050_i2c_read(se050_i2c_hal_t *hal, uint8_t *buffer, int length)
|
|
{
|
|
int fd;
|
|
int retry;
|
|
int bytes_read;
|
|
|
|
if (!hal || !buffer || length <= 0) {
|
|
return -1;
|
|
}
|
|
|
|
fd = (int)(intptr_t)hal->handle;
|
|
|
|
for (retry = 0; retry < SE050_I2C_MAX_RETRY; retry++) {
|
|
bytes_read = read(fd, buffer, length);
|
|
if (bytes_read > 0) {
|
|
return bytes_read;
|
|
}
|
|
|
|
if (errno != EAGAIN && errno != ETIMEDOUT) {
|
|
/* Permanent error */
|
|
break;
|
|
}
|
|
|
|
/* Retry with delay */
|
|
se050_sleep_ms(SE050_WAKEUP_DELAY_MS);
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int se050_i2c_write(se050_i2c_hal_t *hal, const uint8_t *buffer, int length)
|
|
{
|
|
int fd;
|
|
int retry;
|
|
int bytes_written;
|
|
|
|
if (!hal || !buffer || length <= 0) {
|
|
return -1;
|
|
}
|
|
|
|
fd = (int)(intptr_t)hal->handle;
|
|
|
|
for (retry = 0; retry < SE050_I2C_MAX_RETRY; retry++) {
|
|
bytes_written = write(fd, buffer, length);
|
|
if (bytes_written > 0) {
|
|
return bytes_written;
|
|
}
|
|
|
|
if (errno != EAGAIN && errno != ETIMEDOUT) {
|
|
/* Permanent error */
|
|
break;
|
|
}
|
|
|
|
/* Retry with delay */
|
|
se050_sleep_ms(SE050_WAKEUP_DELAY_MS);
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
se050_status_t se050_i2c_wakeup(se050_i2c_hal_t *hal)
|
|
{
|
|
uint8_t wakeup_cmd = 0x00;
|
|
int ret;
|
|
|
|
if (!hal) {
|
|
return SE050_ERR_INVALID_ARG;
|
|
}
|
|
|
|
/* Send wakeup command (dummy write) */
|
|
ret = se050_i2c_write(hal, &wakeup_cmd, 1);
|
|
if (ret < 0) {
|
|
return SE050_ERR_I2C;
|
|
}
|
|
|
|
/* Wait for SE050 to respond */
|
|
se050_sleep_ms(SE050_WAKEUP_DELAY_MS);
|
|
|
|
return SE050_OK;
|
|
}
|