summaryrefslogtreecommitdiff
path: root/zephyr/emul/emul_smart_battery.c
diff options
context:
space:
mode:
Diffstat (limited to 'zephyr/emul/emul_smart_battery.c')
-rw-r--r--zephyr/emul/emul_smart_battery.c356
1 files changed, 127 insertions, 229 deletions
diff --git a/zephyr/emul/emul_smart_battery.c b/zephyr/emul/emul_smart_battery.c
index 392b9a292b..f7e13624cb 100644
--- a/zephyr/emul/emul_smart_battery.c
+++ b/zephyr/emul/emul_smart_battery.c
@@ -14,52 +14,29 @@ LOG_MODULE_REGISTER(smart_battery);
#include <drivers/i2c.h>
#include <drivers/i2c_emul.h>
+#include "emul/emul_common_i2c.h"
#include "emul/emul_smart_battery.h"
#include "crc8.h"
#include "battery_smart.h"
+#define SBAT_DATA_FROM_I2C_EMUL(_emul) \
+ CONTAINER_OF(CONTAINER_OF(_emul, struct i2c_common_emul_data, emul), \
+ struct sbat_emul_data, common)
+
/** Run-time data used by the emulator */
struct sbat_emul_data {
- /** I2C emulator detail */
- struct i2c_emul emul;
- /** Smart battery device being emulated */
- const struct device *i2c;
- /** Configuration information */
- const struct sbat_emul_cfg *cfg;
+ /** Common I2C data */
+ struct i2c_common_emul_data common;
+
/** Data required to simulate battery */
struct sbat_emul_bat_data bat;
/** Command that should be handled next */
int cur_cmd;
/** Message buffer which is used to handle smb transactions */
uint8_t msg_buf[MSG_BUF_LEN];
- /** Position in msg_buf if there is on going smb write opperation */
- int ong_write;
- /** Position in msg_buf if there is on going smb read opperation */
- int ong_read;
/** Total bytes that were generated in response to smb read operation */
int num_to_read;
- /** Custom write function called on smb write opperation */
- sbat_emul_custom_func write_custom_func;
- /** Data passed to custom write function */
- void *write_custom_func_data;
- /** Custom read function called on smb read opperation */
- sbat_emul_custom_func read_custom_func;
- /** Data passed to custom read function */
- void *read_custom_func_data;
-
- /** Mutex used to control access to battery data */
- struct k_mutex bat_mtx;
-};
-
-/** Static configuration for the emulator */
-struct sbat_emul_cfg {
- /** Label of the I2C bus this emulator connects to */
- const char *i2c_label;
- /** Pointer to run-time data */
- struct sbat_emul_data *data;
- /** Address of smart battery on i2c bus */
- uint16_t addr;
};
/** Check description in emul_smart_battery.h */
@@ -67,54 +44,12 @@ struct sbat_emul_bat_data *sbat_emul_get_bat_data(struct i2c_emul *emul)
{
struct sbat_emul_data *data;
- data = CONTAINER_OF(emul, struct sbat_emul_data, emul);
+ data = SBAT_DATA_FROM_I2C_EMUL(emul);
return &data->bat;
}
/** Check description in emul_smart_battery.h */
-int sbat_emul_lock_bat_data(struct i2c_emul *emul, k_timeout_t timeout)
-{
- struct sbat_emul_data *data;
-
- data = CONTAINER_OF(emul, struct sbat_emul_data, emul);
-
- return k_mutex_lock(&data->bat_mtx, timeout);
-}
-
-/** Check description in emul_smart_battery.h */
-int sbat_emul_unlock_bat_dat(struct i2c_emul *emul)
-{
- struct sbat_emul_data *data;
-
- data = CONTAINER_OF(emul, struct sbat_emul_data, emul);
-
- return k_mutex_unlock(&data->bat_mtx);
-}
-
-/** Check description in emul_smart_battery.h */
-void sbat_emul_set_custom_write_func(struct i2c_emul *emul,
- sbat_emul_custom_func func, void *data)
-{
- struct sbat_emul_data *emul_data;
-
- emul_data = CONTAINER_OF(emul, struct sbat_emul_data, emul);
- emul_data->write_custom_func = func;
- emul_data->write_custom_func_data = data;
-}
-
-/** Check description in emul_smart_battery.h */
-void sbat_emul_set_custom_read_func(struct i2c_emul *emul,
- sbat_emul_custom_func func, void *data)
-{
- struct sbat_emul_data *emul_data;
-
- emul_data = CONTAINER_OF(emul, struct sbat_emul_data, emul);
- emul_data->read_custom_func = func;
- emul_data->read_custom_func_data = data;
-}
-
-/** Check description in emul_smart_battery.h */
uint16_t sbat_emul_date_to_word(unsigned int day, unsigned int month,
unsigned int year)
{
@@ -345,7 +280,7 @@ static uint16_t sbat_emul_read_status(struct i2c_emul *emul)
struct sbat_emul_bat_data *bat;
struct sbat_emul_data *data;
- data = CONTAINER_OF(emul, struct sbat_emul_data, emul);
+ data = SBAT_DATA_FROM_I2C_EMUL(emul);
bat = &data->bat;
status = bat->status;
@@ -401,7 +336,7 @@ int sbat_emul_get_word_val(struct i2c_emul *emul, int cmd, uint16_t *val)
int mode_mw;
int rate;
- data = CONTAINER_OF(emul, struct sbat_emul_data, emul);
+ data = SBAT_DATA_FROM_I2C_EMUL(emul);
bat = &data->bat;
mode_mw = bat->mode & MODE_CAPACITY;
@@ -537,7 +472,7 @@ int sbat_emul_get_block_data(struct i2c_emul *emul, int cmd, uint8_t **blk,
struct sbat_emul_bat_data *bat;
struct sbat_emul_data *data;
- data = CONTAINER_OF(emul, struct sbat_emul_data, emul);
+ data = SBAT_DATA_FROM_I2C_EMUL(emul);
bat = &data->bat;
switch (cmd) {
@@ -567,20 +502,41 @@ int sbat_emul_get_block_data(struct i2c_emul *emul, int cmd, uint8_t **blk,
* @brief Append PEC to read command response if battery support it
*
* @param data Pointer to smart battery emulator data
+ * @param cmd Command for which PEC is calculated
*/
-static void sbat_emul_append_pec(struct sbat_emul_data *data)
+static void sbat_emul_append_pec(struct sbat_emul_data *data, int cmd)
{
uint8_t pec;
if (BATTERY_SPEC_VERSION(data->bat.spec_info) ==
BATTERY_SPEC_VER_1_1_WITH_PEC) {
- pec = sbat_emul_pec_head(data->cfg->addr, 1, data->cur_cmd);
+ pec = sbat_emul_pec_head(data->common.cfg->addr, 1, cmd);
pec = cros_crc8_arg(data->msg_buf, data->num_to_read, pec);
data->msg_buf[data->num_to_read] = pec;
data->num_to_read++;
}
}
+/** Check description in emul_smart_battery.h */
+void sbat_emul_set_response(struct i2c_emul *emul, int cmd, uint8_t *buf,
+ int len, bool fail)
+{
+ struct sbat_emul_data *data;
+
+ data = SBAT_DATA_FROM_I2C_EMUL(emul);
+
+ if (fail) {
+ data->bat.error_code = STATUS_CODE_UNKNOWN_ERROR;
+ data->num_to_read = 0;
+ return;
+ }
+
+ data->num_to_read = MIN(len, MSG_BUF_LEN - 1);
+ memcpy(data->msg_buf, buf, data->num_to_read);
+ data->bat.error_code = STATUS_CODE_OK;
+ sbat_emul_append_pec(data, cmd);
+}
+
/**
* @brief Function which handles read messages. It expects that data->cur_cmd
* is set to command number which should be handled. It guarantee that
@@ -589,45 +545,32 @@ static void sbat_emul_append_pec(struct sbat_emul_data *data)
* always set to 0.
*
* @param emul Pointer to smart battery emulator
+ * @param reg Command selected by last write message. If data->cur_cmd is
+ * different than SBAT_EMUL_NO_CMD, then reg should equal to
+ * data->cur_cmd
*
* @return 0 on success
* @return -EIO on error
*/
-static int sbat_emul_handle_read_msg(struct i2c_emul *emul)
+static int sbat_emul_handle_read_msg(struct i2c_emul *emul, int reg)
{
struct sbat_emul_data *data;
uint16_t word;
uint8_t *blk;
int ret, len;
- data = CONTAINER_OF(emul, struct sbat_emul_data, emul);
-
- data->num_to_read = 0;
- if (data->read_custom_func != NULL) {
- ret = data->read_custom_func(emul, data->msg_buf,
- &data->num_to_read, data->cur_cmd,
- data->read_custom_func_data);
- if (ret < 0) {
- data->bat.error_code = STATUS_CODE_UNKNOWN_ERROR;
- data->num_to_read = 0;
-
- return -EIO;
- } else if (ret == 0) {
- data->bat.error_code = STATUS_CODE_OK;
- sbat_emul_append_pec(data);
-
- return 0;
- }
- }
+ data = SBAT_DATA_FROM_I2C_EMUL(emul);
if (data->cur_cmd == SBAT_EMUL_NO_CMD) {
/* Unexpected read message without preceding command select */
data->bat.error_code = STATUS_CODE_UNKNOWN_ERROR;
return -EIO;
}
+ data->cur_cmd = SBAT_EMUL_NO_CMD;
+ data->num_to_read = 0;
/* Handle commands which return word */
- ret = sbat_emul_get_word_val(emul, data->cur_cmd, &word);
+ ret = sbat_emul_get_word_val(emul, reg, &word);
if (ret < 0) {
return -EIO;
}
@@ -636,17 +579,17 @@ static int sbat_emul_handle_read_msg(struct i2c_emul *emul)
data->msg_buf[0] = word & 0xff;
data->msg_buf[1] = (word >> 8) & 0xff;
data->bat.error_code = STATUS_CODE_OK;
- sbat_emul_append_pec(data);
+ sbat_emul_append_pec(data, reg);
return 0;
}
/* Handle commands which return block */
- ret = sbat_emul_get_block_data(emul, data->cur_cmd, &blk, &len);
+ ret = sbat_emul_get_block_data(emul, reg, &blk, &len);
if (ret != 0) {
if (ret == 1) {
data->bat.error_code = STATUS_CODE_UNSUPPORTED;
- LOG_ERR("Unknown read command (0x%x)", data->cur_cmd);
+ LOG_ERR("Unknown read command (0x%x)", reg);
}
return -EIO;
@@ -656,88 +599,65 @@ static int sbat_emul_handle_read_msg(struct i2c_emul *emul)
data->msg_buf[0] = len;
memcpy(&data->msg_buf[1], blk, len);
data->bat.error_code = STATUS_CODE_OK;
- sbat_emul_append_pec(data);
+ sbat_emul_append_pec(data, reg);
return 0;
}
/**
- * @brief Function which finalize write messages. It expects that
- * data->ong_write is set to number of bytes received in data->msg_buf.
- * It guarantee that data->ong_write is set to 0.
+ * @brief Function which finalize write messages.
*
* @param emul Pointer to smart battery emulator
+ * @param reg First byte of write message, usually selected command
+ * @param bytes Number of bytes received in data->msg_buf
*
* @return 0 on success
* @return -EIO on error
*/
-static int sbat_emul_finalize_write_msg(struct i2c_emul *emul)
+static int sbat_emul_finalize_write_msg(struct i2c_emul *emul, int reg,
+ int bytes)
{
struct sbat_emul_bat_data *bat;
struct sbat_emul_data *data;
uint16_t word;
uint8_t pec;
- int ret;
- data = CONTAINER_OF(emul, struct sbat_emul_data, emul);
+ data = SBAT_DATA_FROM_I2C_EMUL(emul);
bat = &data->bat;
- if (data->write_custom_func != NULL) {
- ret = data->write_custom_func(emul, data->msg_buf,
- &data->ong_write,
- data->msg_buf[0],
- data->write_custom_func_data);
- if (ret < 0) {
- data->bat.error_code = STATUS_CODE_UNKNOWN_ERROR;
- data->ong_write = 0;
-
- return -EIO;
- } else if (ret == 0) {
- data->bat.error_code = STATUS_CODE_OK;
- data->ong_write = 0;
-
- return 0;
- }
- }
-
/*
* Fail if:
* - there are no bytes to handle
* - there are too many bytes
* - there is command byte and only one data byte
*/
- if (data->ong_write <= 0 ||
- data->ong_write > 4 ||
- data->ong_write == 2) {
+ if (bytes <= 0 || bytes > 4 || bytes == 2) {
data->bat.error_code = STATUS_CODE_BADSIZE;
- data->ong_write = 0;
- LOG_ERR("wrong write message size (%d)", data->ong_write);
+ LOG_ERR("wrong write message size (%d)", bytes);
return -EIO;
}
/* There is only command for read */
- if (data->ong_write == 1) {
- data->cur_cmd = data->msg_buf[0];
- data->ong_write = 0;
+ if (bytes == 1) {
+ data->cur_cmd = reg;
return 0;
}
/* Handle PEC */
- if (data->ong_write == 4) {
+ data->msg_buf[0] = reg;
+ if (bytes == 4) {
if (BATTERY_SPEC_VERSION(data->bat.spec_info) !=
BATTERY_SPEC_VER_1_1_WITH_PEC) {
data->bat.error_code = STATUS_CODE_BADSIZE;
- data->ong_write = 0;
LOG_ERR("Unexpected PEC; No support in this version");
return -EIO;
}
- pec = sbat_emul_pec_head(data->cfg->addr, 0, 0);
+ pec = sbat_emul_pec_head(data->common.cfg->addr, 0, 0);
pec = cros_crc8_arg(data->msg_buf, 3, pec);
if (pec != data->msg_buf[3]) {
data->bat.error_code = STATUS_CODE_UNKNOWN_ERROR;
- data->ong_write = 0;
LOG_ERR("Wrong PEC 0x%x != 0x%x",
pec, data->msg_buf[3]);
@@ -766,117 +686,88 @@ static int sbat_emul_finalize_write_msg(struct i2c_emul *emul)
bat->at_rate = word;
break;
default:
- data->ong_write = 0;
data->bat.error_code = STATUS_CODE_ACCESS_DENIED;
LOG_ERR("Unknown write command (0x%x)", data->msg_buf[0]);
return -EIO;
}
- data->ong_write = 0;
data->bat.error_code = STATUS_CODE_OK;
return 0;
}
/**
- * Emulator an I2C transfer to an smart battery
+ * @brief Function called for each byte of write message which is saved in
+ * data->msg_buf
*
- * This handles simple reads and writes
- *
- * @param emul I2C emulation information
- * @param msgs List of messages to process. For 'read' messages, this function
- * updates the 'buf' member with the data that was read
- * @param num_msgs Number of messages to process
- * @param addr Address of the I2C target device.
+ * @param emul Pointer to smart battery emulator
+ * @param reg First byte of write message, usually selected command
+ * @param val Received byte of write message
+ * @param bytes Number of bytes already received
*
- * @retval 0 If successful
- * @retval -EIO General input / output error
+ * @return 0 on success
*/
-static int sbat_emul_transfer(struct i2c_emul *emul, struct i2c_msg *msgs,
- int num_msgs, int addr)
+static int sbat_emul_write_byte(struct i2c_emul *emul, int reg, uint8_t val,
+ int bytes)
{
- const struct sbat_emul_cfg *cfg;
struct sbat_emul_data *data;
- int ret = 0, i;
- data = CONTAINER_OF(emul, struct sbat_emul_data, emul);
- cfg = data->cfg;
+ data = SBAT_DATA_FROM_I2C_EMUL(emul);
- if (cfg->addr != addr) {
- LOG_ERR("Address mismatch, expected %02x, got %02x", cfg->addr,
- addr);
- return -EIO;
+ if (bytes < MSG_BUF_LEN) {
+ data->msg_buf[bytes] = val;
}
- i2c_dump_msgs("emul", msgs, num_msgs, addr);
-
- for (; num_msgs > 0; num_msgs--, msgs++) {
- if (!(msgs->flags & I2C_MSG_READ)) {
- /* Disscard any ongoing read */
- data->num_to_read = 0;
-
- /* Save incoming data to buffer */
- for (i = 0; i < msgs->len; i++, data->ong_write++) {
- if (data->ong_write < MSG_BUF_LEN) {
- data->msg_buf[data->ong_write] =
- msgs->buf[i];
- }
- }
-
- /* Handle write message when we receive stop signal */
- if (msgs->flags & I2C_MSG_STOP) {
- k_mutex_lock(&data->bat_mtx, K_FOREVER);
- ret = sbat_emul_finalize_write_msg(emul);
- k_mutex_unlock(&data->bat_mtx);
- }
- } else {
- /* Finalize any ongoing write message before read */
- if (data->ong_write) {
- k_mutex_lock(&data->bat_mtx, K_FOREVER);
- ret = sbat_emul_finalize_write_msg(emul);
- k_mutex_unlock(&data->bat_mtx);
- if (ret) {
- return -EIO;
- }
- }
-
- /* Prepare read message */
- if (!data->num_to_read) {
- k_mutex_lock(&data->bat_mtx, K_FOREVER);
- ret = sbat_emul_handle_read_msg(emul);
- k_mutex_unlock(&data->bat_mtx);
- data->cur_cmd = SBAT_EMUL_NO_CMD;
- data->ong_read = 0;
- }
-
- for (i = 0; i < msgs->len; i++, data->ong_read++) {
- if (data->ong_read >= data->num_to_read) {
- /* We wrote everything */
- data->num_to_read = 0;
- break;
- }
- msgs->buf[i] = data->msg_buf[data->ong_read];
- }
-
- if (msgs->flags & I2C_MSG_STOP) {
- /* Disscard any data that wern't read */
- data->num_to_read = 0;
- }
- }
+ return 0;
+}
- if (ret) {
- return -EIO;
- }
+/**
+ * @brief Function called for each byte of read message. Byte from data->msg_buf
+ * is copied to read message response.
+ *
+ * @param emul Pointer to smart battery emulator
+ * @param reg First byte of last write message, usually selected command
+ * @param val Pointer where byte to read should be stored
+ * @param bytes Number of bytes already readed
+ *
+ * @return 0 on success
+ */
+static int sbat_emul_read_byte(struct i2c_emul *emul, int reg, uint8_t *val,
+ int bytes)
+{
+ struct sbat_emul_data *data;
+
+ data = SBAT_DATA_FROM_I2C_EMUL(emul);
+
+ if (bytes < data->num_to_read) {
+ *val = data->msg_buf[bytes];
}
return 0;
}
+/**
+ * @brief Get currently accessed register, which always equals to selected
+ * command.
+ *
+ * @param emul Pointer to smart battery emulator
+ * @param reg First byte of last write message, usually selected command
+ * @param bytes Number of bytes already handled from current message
+ * @param read If currently handled is read message
+ *
+ * @return Currently accessed register
+ */
+static int sbat_emul_access_reg(struct i2c_emul *emul, int reg, int bytes,
+ bool read)
+{
+ return reg;
+}
+
/* Device instantiation */
static struct i2c_emul_api sbat_emul_api = {
- .transfer = sbat_emul_transfer,
+ .transfer = i2c_common_emul_transfer,
};
/**
@@ -893,15 +784,15 @@ static struct i2c_emul_api sbat_emul_api = {
static int sbat_emul_init(const struct emul *emul,
const struct device *parent)
{
- const struct sbat_emul_cfg *cfg = emul->cfg;
- struct sbat_emul_data *data = cfg->data;
+ const struct i2c_common_emul_cfg *cfg = emul->cfg;
+ struct i2c_common_emul_data *data = cfg->data;
int ret;
data->emul.api = &sbat_emul_api;
data->emul.addr = cfg->addr;
data->i2c = parent;
data->cfg = cfg;
- k_mutex_init(&data->bat_mtx);
+ i2c_common_emul_init(data);
ret = i2c_emul_register(parent, emul->dev_label, &data->emul);
@@ -965,13 +856,20 @@ static int sbat_emul_init(const struct emul *emul,
.error_code = STATUS_CODE_OK, \
}, \
.cur_cmd = SBAT_EMUL_NO_CMD, \
- .write_custom_func = NULL, \
- .read_custom_func = NULL, \
+ .common = { \
+ .start_write = NULL, \
+ .write_byte = sbat_emul_write_byte, \
+ .finish_write = sbat_emul_finalize_write_msg, \
+ .start_read = sbat_emul_handle_read_msg, \
+ .read_byte = sbat_emul_read_byte, \
+ .finish_read = NULL, \
+ .access_reg = sbat_emul_access_reg, \
+ }, \
}; \
\
- static const struct sbat_emul_cfg sbat_emul_cfg_##n = { \
+ static const struct i2c_common_emul_cfg sbat_emul_cfg_##n = { \
.i2c_label = DT_INST_BUS_LABEL(n), \
- .data = &sbat_emul_data_##n, \
+ .data = &sbat_emul_data_##n.common, \
.addr = DT_INST_REG_ADDR(n), \
}; \
EMUL_DEFINE(sbat_emul_init, DT_DRV_INST(n), &sbat_emul_cfg_##n)
@@ -979,7 +877,7 @@ static int sbat_emul_init(const struct emul *emul,
DT_INST_FOREACH_STATUS_OKAY(SMART_BATTERY_EMUL)
#define SMART_BATTERY_EMUL_CASE(n) \
- case DT_INST_DEP_ORD(n): return &sbat_emul_data_##n.emul;
+ case DT_INST_DEP_ORD(n): return &sbat_emul_data_##n.common.emul;
/** Check description in emul_smart_battery.h */
struct i2c_emul *sbat_emul_get_ptr(int ord)