From cef7c5107d2b6c666c3a73b1229179cf594b4264 Mon Sep 17 00:00:00 2001 From: Pim van Pelt Date: Mon, 2 Apr 2018 13:30:45 +0200 Subject: [PATCH] Mock out mgos_i2c.h to work with Linux. --- .gitignore | 2 + Makefile | 32 +++++++ include/mgos.h | 14 +++ include/mgos_i2c.h | 117 +++++++++++++++++++++++ include/mgos_mock.h | 35 +++++++ src/main.c | 20 ++++ src/mgos_i2c.c | 227 ++++++++++++++++++++++++++++++++++++++++++++ src/mgos_mock.c | 44 +++++++++ 8 files changed, 491 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 include/mgos.h create mode 100644 include/mgos_i2c.h create mode 100644 include/mgos_mock.h create mode 100644 src/main.c create mode 100644 src/mgos_i2c.c create mode 100644 src/mgos_mock.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cbd3804 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +build/ +mgos_i2c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..7a2cf2d --- /dev/null +++ b/Makefile @@ -0,0 +1,32 @@ +TARGET = mgos_i2c +CC = gcc +CFLAGS = -g -O -Wall -I include/ +LINKER = gcc +LFLAGS = -O -Wall -I. -lm + +.PHONY: default all clean + +default: $(TARGET) +all: default + +SRCDIR = src +INCDIR = include +OBJDIR = build +BINDIR = . + +SOURCES := $(wildcard $(SRCDIR)/*.c) +INCLUDES := $(wildcard $(SRCDIR)/*.h) +OBJECTS := $(SOURCES:$(SRCDIR)/%.c=$(OBJDIR)/%.o) +RM = rm -f + + +$(BINDIR)/$(TARGET): $(OBJECTS) + $(LINKER) $(OBJECTS) $(LFLAGS) -o $@ + +$(OBJECTS): $(OBJDIR)/%.o : $(SRCDIR)/%.c + $(CC) $(CFLAGS) -c $< -o $@ + +.PHONY: clean +clean: + $(RM) $(OBJECTS) + $(RM) $(BINDIR)/$(TARGET) diff --git a/include/mgos.h b/include/mgos.h new file mode 100644 index 0000000..8c4e194 --- /dev/null +++ b/include/mgos.h @@ -0,0 +1,14 @@ +#ifndef __MGOS_H +#define __MGOS_H + +#include +#include +#include +#include +#include +#include +#include + +#include "mgos_mock.h" + +#endif // __MGOS_H diff --git a/include/mgos_i2c.h b/include/mgos_i2c.h new file mode 100644 index 0000000..2b17bb5 --- /dev/null +++ b/include/mgos_i2c.h @@ -0,0 +1,117 @@ +/* + * Copyright 2015 Cesanta + * All rights reserved + */ + +/* + * I2C API. + * + * See https://en.wikipedia.org/wiki/I%C2%B2C for the background information. + */ + +#pragma once + +#include "mgos.h" + +/* Each platform defines its own I2C connection parameters. */ +struct mgos_i2c; + +/* If this special address is passed to read or write, START is not generated + * and address is not put on the bus. It is assumed that this is a continuation + * of a previous operation which (after read or write with stop = false). */ +#define MGOS_I2C_ADDR_CONTINUE ((uint16_t) -1) + +/* + * Read specified number of bytes from the specified address. + * Address should not include the R/W bit. If addr is -1, START is not + * performed. + * If |stop| is true, then at the end of the operation bus will be released. + */ +bool mgos_i2c_read(struct mgos_i2c *i2c, uint16_t addr, void *data, size_t len, + bool stop); + +/* + * Write specified number of bytes from the specified address. + * Address should not include the R/W bit. If addr is -1, START is not + * performed. + * If |stop| is true, then at the end of the operation bus will be released. + */ +bool mgos_i2c_write(struct mgos_i2c *i2c, uint16_t addr, const void *data, + size_t len, bool stop); + +/* + * Release the bus (when left unreleased after read or write). + */ +void mgos_i2c_stop(struct mgos_i2c *i2c); + +/* Most implementations should support these two, support for other frequencies + * is platform-dependent. */ +#define MGOS_I2C_FREQ_100KHZ 100000 +#define MGOS_I2C_FREQ_400KHZ 400000 + +/* + * Get I2C interface frequency. + */ +int mgos_i2c_get_freq(struct mgos_i2c *i2c); + +/* + * Set I2C interface frequency. + */ +bool mgos_i2c_set_freq(struct mgos_i2c *i2c, int freq); + +/* + * Helper for reading 1-byte register `reg` from a device at address `addr`. + * In case of success return a numeric byte value from 0x00 to 0xff; otherwise + * return -1. + */ +int mgos_i2c_read_reg_b(struct mgos_i2c *conn, uint16_t addr, uint8_t reg); + +/* + * Helper for reading 2-byte register `reg` from a device at address `addr`. + * In case of success returns a numeric big-endian value: e.g. if 0x01, 0x02 + * was read from a device, 0x0102 will be returned. + * + * In case of error returns -1. + */ +int mgos_i2c_read_reg_w(struct mgos_i2c *conn, uint16_t addr, uint8_t reg); + +/* + * Helper for reading `n`-byte register value from a device. Returns true on + * success, false on error. Data is written to `buf`, which should be large + * enough. + */ +bool mgos_i2c_read_reg_n(struct mgos_i2c *conn, uint16_t addr, uint8_t reg, + size_t n, uint8_t *buf); + +/* + * Helper for writing 1-byte register `reg` to a device at address `addr`. + * Returns `true` in case of success, `false` otherwise. + */ +bool mgos_i2c_write_reg_b(struct mgos_i2c *conn, uint16_t addr, uint8_t reg, + uint8_t value); + +/* + * Helper for writing 2-byte register `reg` to a device at address `addr`. + * The value is big-endian: e.g. if `value` is `0x0102`, then `0x01, 0x02` + * will be written. + * Returns `true` in case of success, `false` otherwise. + */ +bool mgos_i2c_write_reg_w(struct mgos_i2c *conn, uint16_t addr, uint8_t reg, + uint16_t value); + +/* + * Helper for writing `n`-byte register `reg` to a device at address `addr`. + * Returns `true` in case of success, `false` otherwise. + */ +bool mgos_i2c_write_reg_n(struct mgos_i2c *conn, uint16_t addr, uint8_t reg, + size_t n, const uint8_t *buf); + +/* Close i2c connection and free resources. */ +void mgos_i2c_close(struct mgos_i2c *conn); + +/* Return i2c bus handle that is set up via the sysconfig. */ +struct mgos_i2c *mgos_i2c_get_global(void); + +// User contributed! +bool mgos_i2c_open(int busnr); + diff --git a/include/mgos_mock.h b/include/mgos_mock.h new file mode 100644 index 0000000..8503000 --- /dev/null +++ b/include/mgos_mock.h @@ -0,0 +1,35 @@ +#ifndef __MGOS_MOCK_H +#define __MGOS_MOCK_H + +/* Some functions mocked from MGOS, so we can run unit tests standalone. + */ + +#include "mgos.h" +#include +#include + +// mgos_log +enum cs_log_level { + LL_NONE = -1, + LL_ERROR = 0, + LL_WARN = 1, + LL_INFO = 2, + LL_DEBUG = 3, + LL_VERBOSE_DEBUG = 4, + + _LL_MIN = -2, + _LL_MAX = 5, +}; + +int log_print_prefix(enum cs_log_level l, const char *func, const char *file); + +#define LOG(l, x) \ + do { \ + if (log_print_prefix(l, __func__, __FILE__)) printf x; \ + printf("\r\n"); \ + } while (0) + + +float mg_time(); + +#endif // __MGOS_MOCK_H diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..daae1ef --- /dev/null +++ b/src/main.c @@ -0,0 +1,20 @@ +#include "mgos.h" +#include "mgos_i2c.h" + +#define I2CBUSNR 7 + +int main() { + struct mgos_i2c *i2c; + + if (!mgos_i2c_open(I2CBUSNR)) { + LOG(LL_ERROR, ("Cannot open I2C bus %u", I2CBUSNR)); + return -1; + } + i2c = mgos_i2c_get_global(); + if (!i2c) { + LOG(LL_ERROR, ("Cannot open I2C bus")); + return -2; + } + + return 0; +} diff --git a/src/mgos_i2c.c b/src/mgos_i2c.c new file mode 100644 index 0000000..9eecb8b --- /dev/null +++ b/src/mgos_i2c.c @@ -0,0 +1,227 @@ +#include "mgos_i2c.h" +#include +#include +#include +#include +#include +#include +#include + + + +struct mgos_i2c { + int fd; + char *filename; +}; + +static struct mgos_i2c *s_global_i2c_bus = NULL; + +bool mgos_i2c_read(struct mgos_i2c *i2c, uint16_t addr, void *data, size_t len, bool stop) { + size_t ret; + + if (!i2c) { + LOG(LL_ERROR, ("No I2C bus")); + return false; + } + + if (ioctl(i2c->fd,I2C_SLAVE,addr) < 0) { + LOG(LL_ERROR, ("Cannot select slave 0x%04x on I2C bus", addr)); + return false; + } + ret = read(i2c->fd, data, len); + return (ret == len); +} + +bool mgos_i2c_write(struct mgos_i2c *i2c, uint16_t addr, const void *data, size_t len, bool stop) { + size_t ret; + + if (!i2c) { + LOG(LL_ERROR, ("No I2C bus")); + return false; + } + + if (ioctl(i2c->fd,I2C_SLAVE,addr) < 0) { + LOG(LL_ERROR, ("Cannot select slave 0x%04x on I2C bus", addr)); + return false; + } + ret = write(i2c->fd, data, len); + return (ret == len); +} + +void mgos_i2c_stop(struct mgos_i2c *i2c) { + return; +} + +int mgos_i2c_get_freq(struct mgos_i2c *i2c) { + return MGOS_I2C_FREQ_100KHZ; +} + +bool mgos_i2c_set_freq(struct mgos_i2c *i2c, int freq) { + if (freq==MGOS_I2C_FREQ_100KHZ) return true; + return false; +} + +int mgos_i2c_read_reg_b(struct mgos_i2c *i2c, uint16_t addr, uint8_t reg) { + uint8_t data; + + if (!i2c) { + LOG(LL_ERROR, ("No I2C bus")); + return -1; + } + + if (ioctl(i2c->fd,I2C_SLAVE,addr) < 0) { + LOG(LL_ERROR, ("Cannot select slave 0x%04x on I2C bus", addr)); + return -1; + } + if (1 != write(i2c->fd, ®, 1)) { + LOG(LL_ERROR, ("Cannot select register 0x%02x on device 0x%04x", reg, addr)); + return -1; + } + if (1 != read(i2c->fd, &data, 1)) { + LOG(LL_ERROR, ("Cannot read register 0x%02x on device 0x%04x", reg, addr)); + return -1; + } + return data; +} + +int mgos_i2c_read_reg_w(struct mgos_i2c *i2c, uint16_t addr, uint8_t reg) { + uint16_t data; + + if (!i2c) { + LOG(LL_ERROR, ("No I2C bus")); + return -1; + } + + if (ioctl(i2c->fd,I2C_SLAVE,addr) < 0) { + LOG(LL_ERROR, ("Cannot select slave 0x%04x on I2C bus", addr)); + return -1; + } + if (1 != write(i2c->fd, ®, 1)) { + LOG(LL_ERROR, ("Cannot select register 0x%02x on device 0x%04x", reg, addr)); + return -1; + } + if (2 != read(i2c->fd, &data, 2)) { + LOG(LL_ERROR, ("Cannot read register 0x%02x on device 0x%04x", reg, addr)); + return -1; + } + return data; +} + +bool mgos_i2c_read_reg_n(struct mgos_i2c *i2c, uint16_t addr, uint8_t reg, size_t n, uint8_t *buf) { + if (!i2c) { + LOG(LL_ERROR, ("No I2C bus")); + return false; + } + + if (ioctl(i2c->fd,I2C_SLAVE,addr) < 0) { + LOG(LL_ERROR, ("Cannot select slave 0x%04x on I2C bus", addr)); + return false; + } + if (1 != write(i2c->fd, ®, 1)) { + LOG(LL_ERROR, ("Cannot select register 0x%02x on device 0x%04x", reg, addr)); + return false; + } + if (n != read(i2c->fd, buf, n)) { + LOG(LL_ERROR, ("Cannot read %lu bytes at register 0x%02x on device 0x%04x", n, reg, addr)); + return false; + } + return true; +} + +bool mgos_i2c_write_reg_b(struct mgos_i2c *i2c, uint16_t addr, uint8_t reg, uint8_t value) { + uint8_t data[2]; + + if (!i2c) { + LOG(LL_ERROR, ("No I2C bus")); + return false; + } + + if (ioctl(i2c->fd,I2C_SLAVE,addr) < 0) { + LOG(LL_ERROR, ("Cannot select slave 0x%04x on I2C bus", addr)); + return false; + } + data[0]=reg; + data[1]=value; + if (2 != write(i2c->fd, data, 2)) { + LOG(LL_ERROR, ("Cannot write to register 0x%02x on device 0x%04x", reg, addr)); + return false; + } + return true; +} + +bool mgos_i2c_write_reg_w(struct mgos_i2c *i2c, uint16_t addr, uint8_t reg, uint16_t value) { + uint8_t data[3]; + + if (!i2c) { + LOG(LL_ERROR, ("No I2C bus")); + return false; + } + + if (ioctl(i2c->fd,I2C_SLAVE,addr) < 0) { + LOG(LL_ERROR, ("Cannot select slave 0x%04x on I2C bus", addr)); + return false; + } + data[0]=reg; + data[1]=(uint8_t)(0xFF & (value >> 0)); + data[2]=(uint8_t)(0xFF & (value >> 8)); + + if (3 != write(i2c->fd, data, 3)) { + LOG(LL_ERROR, ("Cannot write to register 0x%02x on device 0x%04x", reg, addr)); + return false; + } + return true; +} + +bool mgos_i2c_write_reg_n(struct mgos_i2c *i2c, uint16_t addr, uint8_t reg, size_t n, const uint8_t *buf) { + if (!i2c) { + LOG(LL_ERROR, ("No I2C bus")); + return false; + } + + if (ioctl(i2c->fd,I2C_SLAVE,addr) < 0) { + LOG(LL_ERROR, ("Cannot select slave 0x%04x on I2C bus", addr)); + return false; + } + if (1 != write(i2c->fd, ®, 1)) { + LOG(LL_ERROR, ("Cannot select register 0x%02x on device 0x%04x", reg, addr)); + return false; + } + + if (n != write(i2c->fd, buf, n)) { + LOG(LL_ERROR, ("Cannot write to register 0x%02x on device 0x%04x", reg, addr)); + return false; + } + return true; +} + +void mgos_i2c_close(struct mgos_i2c *i2c) { + return; +} + +struct mgos_i2c *mgos_i2c_get_global(void) { + return s_global_i2c_bus; +} + +// User provided function to interface with Linux I2C driver +bool mgos_i2c_open(int busnr) { + int fd; + char filename[20]; + struct mgos_i2c *i2c; + + sprintf(filename,"/dev/i2c-%d",busnr); + if ((fd = open(filename,O_RDWR)) < 0) { + LOG(LL_ERROR, ("Could not open %s", filename)); + return false; + } + i2c = calloc(1, sizeof(struct mgos_i2c)); + if (!i2c) { + LOG(LL_ERROR, ("Could not allocate mgos_i2c handle for %s", filename)); + return false; + } + i2c->fd=fd; + i2c->filename=strdup(filename); + s_global_i2c_bus=i2c; + LOG(LL_INFO, ("Opened I2C bus on %s", filename)); + + return true; +} diff --git a/src/mgos_mock.c b/src/mgos_mock.c new file mode 100644 index 0000000..8423b09 --- /dev/null +++ b/src/mgos_mock.c @@ -0,0 +1,44 @@ +/* Some functions mocked from MGOS, so we can run unit tests standalone. + */ + +#include "mgos_mock.h" + +int _mgos_timers = 0; + +int log_print_prefix(enum cs_log_level l, const char *func, const char *file) { + char ll_str[6]; + + switch(l) { + case LL_ERROR: + strncpy(ll_str, "ERROR", sizeof(ll_str)); + break; + case LL_WARN: + strncpy(ll_str, "WARN", sizeof(ll_str)); + break; + case LL_INFO: + strncpy(ll_str, "INFO", sizeof(ll_str)); + break; + case LL_DEBUG: + strncpy(ll_str, "DEBUG", sizeof(ll_str)); + break; + case LL_VERBOSE_DEBUG: + strncpy(ll_str, "VERB", sizeof(ll_str)); + break; + default: // LL_NONE + return 0; + } + printf ("%-5s %-20s %-40s| ", ll_str, file, func); + return 1; +} + +float mg_time() { + struct timeval tod; + float time; + + gettimeofday(&tod, NULL); + + time = tod.tv_sec; + time += tod.tv_usec/1000000; + + return time; +}