common/util/hal_i2c_slave.c (335 lines of code) (raw):

/* NAME: I2C SLAVE DEVICE FILE: hal_i2c_slave.c DESCRIPTION: There is 1 callback function "i2c_slave_cb" for I2C slave ISR handle and user APIs for user access. AUTHOR: MouchenHung DATE/VERSION: 2021.12.09 - v1.4.2 Note: (1) Shall not modify code in this file!!! (2) "hal_i2c_slave.h" must be included! (3) User APIs follow check-rule before doing task [api] [.is_init] [.is_register] * i2c_slave_control X X * i2c_slave_read O X * i2c_slave_status_get X X * i2c_slave_status_print X X * i2c_slave_cfg_get O X (O: must equal 1, X: no need to check) (4) I2C slave function/api usage recommend [ACTIVATE] Use "i2c_slave_control()" to register/modify/unregister slave bus [READ] Use "i2c_slave_read()" to read slave queue message (5) Slave queue method: Zephyr api, unregister the bus while full msgq, register back while msgq get space. */ #include <zephyr.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <drivers/i2c.h> #include <sys/printk.h> #include "hal_i2c_slave.h" /* LOG SET */ #include <logging/log.h> #define LOG_LEVEL CONFIG_I2C_LOG_LEVEL LOG_MODULE_REGISTER(mc_i2c_slave); /* I2C slave device arr */ static struct i2c_slave_device i2c_slave_device_global[MAX_SLAVE_NUM] = { 0 }; /* I2C slave config modify lock */ struct k_mutex i2c_slave_mutex[MAX_SLAVE_NUM]; /* static function declare */ static int do_i2c_slave_cfg(uint8_t bus_num, struct _i2c_slave_config *cfg); static int do_i2c_slave_register(uint8_t bus_num); static int do_i2c_slave_unregister(uint8_t bus_num); static int i2c_slave_write_requested(struct i2c_slave_config *config) { struct i2c_slave_data *data; if (!config) { LOG_ERR("Get empty config!"); return 1; } data = CONTAINER_OF(config, struct i2c_slave_data, config); data->current_msg.msg_length = 0; memset(data->current_msg.msg, 0x0, MAX_I2C_SLAVE_BUFF); data->buffer_idx = 0; return 0; } static int i2c_slave_write_received(struct i2c_slave_config *config, uint8_t val) { struct i2c_slave_data *data; if (!config) { LOG_ERR("Get empty config!"); return 1; } data = CONTAINER_OF(config, struct i2c_slave_data, config); if (data->buffer_idx >= MAX_I2C_SLAVE_BUFF) { LOG_ERR("Buffer_idx over limit!"); return 1; } data->current_msg.msg[data->buffer_idx++] = val; return 0; } static int i2c_slave_stop(struct i2c_slave_config *config) { struct i2c_slave_data *data; if (!config) { LOG_ERR("Get empty config!"); return 1; } data = CONTAINER_OF(config, struct i2c_slave_data, config); if (data->buffer_idx) { data->current_msg.msg_length = data->buffer_idx; /* try to put new node to message queue */ uint8_t ret = k_msgq_put(&data->z_msgq_id, &data->current_msg, K_NO_WAIT); if (ret) { LOG_ERR("Can't put new node to message queue on bus[%d], cause of %d", data->i2c_bus, ret); return 1; } /* if slave queue is full, unregister the bus slave to prevent next message handle */ if (!k_msgq_num_free_get(&data->z_msgq_id)) { LOG_DBG("Slave queue is full, unregister bus[%d]", data->i2c_bus); do_i2c_slave_unregister(data->i2c_bus); } } return 0; } static const struct i2c_slave_callbacks i2c_slave_cb = { .write_requested = i2c_slave_write_requested, .read_requested = NULL, .write_received = i2c_slave_write_received, .read_processed = NULL, .stop = i2c_slave_stop, }; /* - Name: i2c_slave_status_get (ESSENTIAL for every user API) - Description: Get current status of i2c slave. - Input: * bus_num: Bus number with zero base - Return: * 0, if no error * others, get error(check "i2c_slave_error_status") */ uint8_t i2c_slave_status_get(uint8_t bus_num) { uint8_t ret = I2C_SLAVE_HAS_NO_ERR; if (bus_num >= MAX_SLAVE_NUM) { ret |= I2C_SLAVE_BUS_INVALID; goto out; } char controllername[10]; if (snprintf(controllername, sizeof(controllername), "%s%d", I2C_DEVICE_PREFIX, bus_num) < 0) { LOG_ERR("I2C controller name parsing error!"); ret = I2C_SLAVE_CONTROLLER_ERR; goto out; } const struct device *tmp_device = device_get_binding(controllername); if (!tmp_device) { ret |= I2C_SLAVE_CONTROLLER_ERR; goto out; } if (!i2c_slave_device_global[bus_num].is_init) { ret |= I2C_SLAVE_NOT_INIT; } if (!i2c_slave_device_global[bus_num].is_register) { ret |= I2C_SLAVE_NOT_REGISTER; } out: return ret; } /* - Name: i2c_slave_cfg_get (OPTIONAL) - Description: Get current cfg of i2c slave. - Input: * bus_num: Bus number with zero base * cfg: cfg structure(controller name is not support!) - Return: * 0, if no error * others, get error(check "i2c_slave_api_error_status") */ uint8_t i2c_slave_cfg_get(uint8_t bus_num, struct _i2c_slave_config *cfg) { uint8_t status; if (!cfg) return I2C_SLAVE_API_INPUT_ERR; /* check input */ status = i2c_slave_status_get(bus_num); if (status & (I2C_SLAVE_BUS_INVALID | I2C_SLAVE_CONTROLLER_ERR | I2C_SLAVE_NOT_INIT)) { LOG_ERR("Bus[%d] check status failed with error status 0x%x!", bus_num, status); return I2C_SLAVE_API_BUS_GET_FAIL; } struct i2c_slave_data *data = &i2c_slave_device_global[bus_num].data; cfg->address = data->config.address; cfg->i2c_msg_count = data->z_msgq_id.max_msgs; return I2C_SLAVE_API_NO_ERR; } /* - Name: i2c_slave_status_print (OPTIONAL|DEBUGUSE) - Description: Get current status of i2c slave queue. - Input: * bus_num: Bus number with zero base - Return: * 0, if no error * others, get error(check "i2c_slave_api_error_status") */ uint8_t i2c_slave_status_print(uint8_t bus_num) { uint8_t status; /* check input */ status = i2c_slave_status_get(bus_num); if (status & (I2C_SLAVE_BUS_INVALID | I2C_SLAVE_CONTROLLER_ERR)) { LOG_ERR("Bus[%d] check status failed with error status 0x%x!", bus_num, status); return I2C_SLAVE_API_BUS_GET_FAIL; } struct i2c_slave_data *data = &i2c_slave_device_global[bus_num].data; struct i2c_slave_device *slave_info = &i2c_slave_device_global[bus_num]; printk("=============================\n"); printk("Slave bus[%d] monitor\n", bus_num); printk("* init: %d\n", slave_info->is_init); printk("* register: %d\n", slave_info->is_register); printk("* address: 0x%x\n", data->config.address); printk("* status: %d/%d\n", k_msgq_num_used_get(&data->z_msgq_id), data->z_msgq_id.max_msgs); printk("=============================\n"); return I2C_SLAVE_API_NO_ERR; } /* - Name: i2c_slave_read - Description: Try to get message from i2c slave message queue. - Input: * bus_num: Bus number with zero base * *buff: Message that readed back from queue * buff_len: Length of buffer * *msg_len: Read-back message's length - Return: * 0, if no error * others, get error(check "i2c_slave_api_error_status") */ uint8_t i2c_slave_read(uint8_t bus_num, uint8_t *buff, uint16_t buff_len, uint16_t *msg_len) { uint8_t status; if (!buff || !msg_len) return I2C_SLAVE_API_INPUT_ERR; /* check input, support while bus slave is unregistered */ status = i2c_slave_status_get(bus_num); if (status & (I2C_SLAVE_BUS_INVALID | I2C_SLAVE_CONTROLLER_ERR | I2C_SLAVE_NOT_INIT)) { LOG_ERR("Bus[%d] check status failed with error status 0x%x!", bus_num, status); return I2C_SLAVE_API_BUS_GET_FAIL; } struct i2c_slave_data *data = &i2c_slave_device_global[bus_num].data; struct i2c_msg_package local_buf; /* wait if there's no any message in message queue */ uint8_t ret = k_msgq_get(&data->z_msgq_id, &local_buf, K_FOREVER); if (ret) { LOG_ERR("Can't get new node from message queue on bus[%d], cause of %d", data->i2c_bus, ret); return I2C_SLAVE_API_MSGQ_ERR; } if (buff_len < local_buf.msg_length) { memcpy(buff, &(local_buf.msg), buff_len); *msg_len = buff_len; } else { memcpy(buff, &(local_buf.msg), local_buf.msg_length); *msg_len = local_buf.msg_length; } /* if bus slave has been unregister cause of queue full previously, then register it on */ if (k_msgq_num_used_get(&data->z_msgq_id) == (data->z_msgq_id.max_msgs - 1)) { LOG_DBG("Slave queue has available space, register bus[%d]", data->i2c_bus); if (do_i2c_slave_register(bus_num)) { LOG_ERR("Slave queue register bus[%d] failed!", data->i2c_bus); return I2C_SLAVE_API_BUS_GET_FAIL; } } return I2C_SLAVE_API_NO_ERR; } /* - Name: i2c_slave_control - Description: Register controller for user api. - Input: * bus_num: Bus number with zero base * *cfg: Config settings structure * mode: check "i2c_slave_api_control_mode" - Return: * 0, if no error * others, get error(check "i2c_slave_api_error_status") */ int i2c_slave_control(uint8_t bus_num, struct _i2c_slave_config *cfg, enum i2c_slave_api_control_mode mode) { int status; /* Check input and slave status */ uint8_t slave_status = i2c_slave_status_get(bus_num); if (slave_status & (I2C_SLAVE_BUS_INVALID | I2C_SLAVE_CONTROLLER_ERR)) { LOG_ERR("Bus[%d] check status failed with error status 0x%x!", bus_num, slave_status); return I2C_SLAVE_API_BUS_GET_FAIL; } switch (mode) { /* Case1: do config then register (if already config before, then modify config set) */ case I2C_CONTROL_REGISTER: if (!cfg) { return I2C_SLAVE_API_INPUT_ERR; } status = do_i2c_slave_cfg(bus_num, cfg); if (status) { LOG_ERR("Bus[%d] config failed with errorcode %d!", bus_num, status); return status; } status = do_i2c_slave_register(bus_num); if (status) { LOG_ERR("Bus[%d] register failed with errorcode %d!", bus_num, status); return status; } break; /* Case2: do unregister only, config not affected */ case I2C_CONTROL_UNREGISTER: status = do_i2c_slave_unregister(bus_num); if (status) { LOG_ERR("Bus[%d] unregister failed with errorcode %d!", bus_num, status); return status; } break; default: return I2C_SLAVE_API_INPUT_ERR; } return I2C_SLAVE_API_NO_ERR; } /* - Name: do_i2c_slave_cfg - Description: To initialize I2C slave config, or modify config after initialized. - Input: * bus_num: Bus number with zero base * *bus_name: Bus controler name with string(it's used to get binding from certain zephyr device tree driver) * slave_address: Given a slave adress for BIC itself * _max_msg_count: Maximum count of messages in message queue - Return: * 0, if no error * others, get error(check "i2c_slave_api_error_status") */ static int do_i2c_slave_cfg(uint8_t bus_num, struct _i2c_slave_config *cfg) { if (!cfg) return I2C_SLAVE_API_INPUT_ERR; int status; uint8_t slave_status = I2C_SLAVE_HAS_NO_ERR; int ret = I2C_SLAVE_API_NO_ERR; /* check input, support while bus slave is unregistered */ slave_status = i2c_slave_status_get(bus_num); if (slave_status & (I2C_SLAVE_BUS_INVALID | I2C_SLAVE_CONTROLLER_ERR)) { LOG_ERR("Bus[%d] check status failed with error status 0x%x!", bus_num, slave_status); return I2C_SLAVE_API_BUS_GET_FAIL; } /* need unregister first */ if (!(slave_status & I2C_SLAVE_NOT_REGISTER)) { status = do_i2c_slave_unregister(bus_num); if (status) { LOG_ERR("Slave bus[%d] mutex lock failed!", bus_num); return I2C_SLAVE_API_BUS_GET_FAIL; } } /* Mutex init here */ if (slave_status & I2C_SLAVE_NOT_INIT) { if (k_mutex_init(&i2c_slave_mutex[bus_num])) { LOG_ERR("Slave bus[%d] mutex init - failed!", bus_num); return I2C_SLAVE_API_LOCK_ERR; } LOG_DBG("Slave bus[%d] mutex init - success!", bus_num); } if (k_mutex_lock(&i2c_slave_mutex[bus_num], K_MSEC(1000))) { LOG_ERR("Slave bus[%d] mutex lock failed!", bus_num); return I2C_SLAVE_API_LOCK_ERR; } uint8_t slave_address = cfg->address; uint16_t _max_msg_count = cfg->i2c_msg_count; struct i2c_slave_data *data = &i2c_slave_device_global[bus_num].data; char *i2C_slave_queue_buffer; /* do init, Only one time init for each bus slave */ if (slave_status & I2C_SLAVE_NOT_INIT) { LOG_DBG("Bus[%d] is going to init!", bus_num); data->i2c_bus = bus_num; char controllername[10]; if (snprintf(controllername, sizeof(controllername), "%s%d", I2C_DEVICE_PREFIX, bus_num) < 0) { LOG_ERR("I2C controller name parsing error!"); ret = I2C_SLAVE_API_MEMORY_ERR; goto unlock; } data->i2c_controller = device_get_binding(controllername); if (!data->i2c_controller) { LOG_ERR("I2C controller not found!"); ret = -EINVAL; goto unlock; } data->config.callbacks = &i2c_slave_cb; } /* do modify, modify config set after init */ else { LOG_DBG("Bus[%d] is going to modified!", bus_num); k_msgq_purge(&data->z_msgq_id); if (data->z_msgq_id.buffer_start != NULL) { free(data->z_msgq_id.buffer_start); data->z_msgq_id.buffer_start = NULL; } } data->max_msg_count = _max_msg_count; data->config.address = slave_address >> 1; // to 7-bit slave address i2C_slave_queue_buffer = malloc(data->max_msg_count * sizeof(struct i2c_msg_package)); if (!i2C_slave_queue_buffer) { LOG_ERR("I2C slave bus[%d] msg queue memory allocate failed!", data->i2c_bus); ret = I2C_SLAVE_API_MEMORY_ERR; goto unlock; } k_msgq_init(&data->z_msgq_id, i2C_slave_queue_buffer, sizeof(struct i2c_msg_package), data->max_msg_count); i2c_slave_device_global[bus_num].is_init = 1; LOG_DBG("I2C slave bus[%d] message queue create success with count %d!", data->i2c_bus, data->max_msg_count); unlock: if (k_mutex_unlock(&i2c_slave_mutex[bus_num])) { LOG_ERR("Mutex unlock failed!"); } return ret; } /* - Name: do_i2c_slave_register - Description: Set config to register for enable i2c slave. - Input: * bus_num: Bus number with zero base - Return: * 0, if no error * others, get error(check "i2c_slave_api_error_status") */ static int do_i2c_slave_register(uint8_t bus_num) { int ret = 0; /* Check input and slave status */ uint8_t slave_status = i2c_slave_status_get(bus_num); if (slave_status & (I2C_SLAVE_BUS_INVALID | I2C_SLAVE_CONTROLLER_ERR | I2C_SLAVE_NOT_INIT)) { LOG_ERR("Bus[%d] check status failed with error status 0x%x!", bus_num, slave_status); return I2C_SLAVE_API_BUS_GET_FAIL; } /* Check register status */ if (!(slave_status & I2C_SLAVE_NOT_REGISTER)) { LOG_ERR("Bus[%d] has already been registered, please unregister first!", bus_num); return I2C_SLAVE_API_BUS_GET_FAIL; } struct i2c_slave_data *data = &i2c_slave_device_global[bus_num].data; /* check whether msgq is full */ if (!k_msgq_num_free_get(&data->z_msgq_id)) { LOG_ERR("Bus[%d] msgq is already full, can't register now, please read out message first!", bus_num); return I2C_SLAVE_API_MSGQ_ERR; } ret = i2c_slave_register(data->i2c_controller, &data->config); if (ret) return ret; i2c_slave_device_global[bus_num].is_register = 1; return I2C_SLAVE_API_NO_ERR; } /* - Name: do_i2c_slave_unregister - Description: Set config to register for disable i2c slave. - Input: * bus_num: Bus number with zero base * mutex_flag: skip check if 1, otherwise 0 - Return: * 0, if no error * others, get error(check "i2c_slave_api_error_status") */ static int do_i2c_slave_unregister(uint8_t bus_num) { int ret = 0; /* Check input and slave status */ uint8_t slave_status = i2c_slave_status_get(bus_num); if (slave_status & (I2C_SLAVE_BUS_INVALID | I2C_SLAVE_CONTROLLER_ERR | I2C_SLAVE_NOT_INIT)) { LOG_ERR("Bus[%d] check status failed with error status 0x%x!", bus_num, slave_status); return I2C_SLAVE_API_BUS_GET_FAIL; } /* Check register status */ if (slave_status & I2C_SLAVE_NOT_REGISTER) { LOG_ERR("Bus[%d] has already been unregistered, please register first!", bus_num); return I2C_SLAVE_API_BUS_GET_FAIL; } struct i2c_slave_data *data = &i2c_slave_device_global[bus_num].data; ret = i2c_slave_unregister(data->i2c_controller, &data->config); if (ret) return ret; i2c_slave_device_global[bus_num].is_register = 0; return I2C_SLAVE_API_NO_ERR; }