--- /dev/null
+/* Driver for the BMP085 Preassure and Temperature Sensor */
+
+#include <minix/ds.h>
+#include <minix/drivers.h>
+#include <minix/i2c.h>
+#include <minix/i2cdriver.h>
+#include <minix/chardriver.h>
+#include <minix/log.h>
+
+/* Control Register for triggering a measurement */
+#define CTRL_REG 0xf4
+
+/* temperature sensor - it only has one 'mode' - conversion time 4.5 ms */
+#define CMD_TRIG_T 0x2e
+#define UDELAY_T (4500)
+
+/* pressure sensor - ultra low power mode - conversion time 4.5 ms */
+#define CMD_TRIG_P_ULP 0x34
+#define MODE_ULP 0x00
+#define UDELAY_ULP (4500)
+
+/* pressure sensor - standard mode - conversion time 7.5 ms */
+#define CMD_TRIG_P_STD 0x74
+#define MODE_STD 0x01
+#define UDELAY_STD (7500)
+
+/* pressure sensor - high resolution mode - conversion time 13.5 ms */
+#define CMD_TRIG_P_HR 0xb4
+#define MODE_HR 0x02
+#define UDELAY_HR (13500)
+
+/* pressure sensor - ultra high resolution mode - conversion time 25.5 ms */
+#define CMD_TRIG_P_UHR 0xf4
+#define MODE_UHR 0x03
+#define UDELAY_UHR (25500)
+
+/* Values for the different modes of operation */
+struct pressure_cmd
+{
+ uint8_t cmd;
+ uint8_t mode;
+ uint16_t udelay;
+};
+
+/* Table of available modes and their parameters. */
+static struct pressure_cmd pressure_cmds[4] = {
+ {CMD_TRIG_P_ULP, MODE_ULP, UDELAY_ULP},
+ {CMD_TRIG_P_STD, MODE_STD, UDELAY_STD},
+ {CMD_TRIG_P_HR, MODE_HR, UDELAY_HR},
+ {CMD_TRIG_P_UHR, MODE_UHR, UDELAY_UHR}
+};
+
+/* Default to standard mode.
+ * There isn't code to configure the resolution at runtime, but it should
+ * easy to implement by setting p_cmd to the right element of pressure_cmds.
+ */
+static struct pressure_cmd *p_cmd = &pressure_cmds[MODE_STD];
+
+/* Chip Identification */
+#define CHIPID_REG 0xd0
+#define BMP085_CHIPID 0x55
+
+/*
+ * There is also a version register at 0xd1, but documentation seems to be
+ * lacking. The sample code says high 4 bytes are AL version and low 4 are ML.
+ */
+
+/* Calibration coefficients
+ *
+ * These are unique to each chip and must be read when starting the driver.
+ * Validate them by checking that none are 0x0000 nor 0xffff. Types and
+ * names are from the datasheet.
+ */
+struct calibration
+{
+ int16_t ac1;
+ int16_t ac2;
+ int16_t ac3;
+ uint16_t ac4;
+ uint16_t ac5;
+ uint16_t ac6;
+ int16_t b1;
+ int16_t b2;
+ int16_t mb;
+ int16_t mc;
+ int16_t md;
+} cal;
+
+/* Register locations for calibration coefficients */
+#define AC1_MSB_REG 0xaa
+#define AC1_LSB_REG 0xab
+#define AC2_MSB_REG 0xac
+#define AC2_LSB_REG 0xad
+#define AC3_MSB_REG 0xae
+#define AC3_LSB_REG 0xaf
+#define AC4_MSB_REG 0xb0
+#define AC4_LSB_REG 0xb1
+#define AC5_MSB_REG 0xb2
+#define AC5_LSB_REG 0xb3
+#define AC6_MSB_REG 0xb4
+#define AC6_LSB_REG 0xb5
+#define B1_MSB_REG 0xb6
+#define B1_LSB_REG 0xb7
+#define B2_MSB_REG 0xb8
+#define B2_LSB_REG 0xb9
+#define MB_MSB_REG 0xba
+#define MB_LSB_REG 0xbb
+#define MC_MSB_REG 0xbc
+#define MC_LSB_REG 0xbd
+#define MD_MSB_REG 0xbe
+#define MD_LSB_REG 0xbf
+
+#define CAL_COEF_FIRST AC1_MSB_REG
+#define CAL_COEF_LAST MD_LSB_REG
+
+#define CAL_COEF_IS_VALID(x) (x != 0x0000 && x != 0xffff)
+
+#define SENSOR_VAL_MSB_REG 0xf6
+#define SENSOR_VAL_LSB_REG 0xf7
+#define SENSOR_VAL_XLSB_REG 0xf8
+
+/* logging - use with log_warn(), log_info(), log_debug(), log_trace(), etc */
+static struct log log = {
+ .name = "bmp085",
+ .log_level = LEVEL_INFO,
+ .log_func = default_log
+};
+
+/* Only one valid slave address. It isn't configurable. */
+static i2c_addr_t valid_addrs[5] = {
+ 0x77, 0x00
+};
+
+/* Buffer to store output string returned when reading from device file. */
+#define BUFFER_LEN 64
+char buffer[BUFFER_LEN + 1];
+
+/* the bus that this device is on (counting starting at 1) */
+static uint32_t bus;
+
+/* slave address of the device */
+static i2c_addr_t address;
+
+/* endpoint for the driver for the bus itself. */
+static endpoint_t bus_endpoint;
+
+/* main device functions */
+static int bmp085_init(void);
+static int version_check(void);
+static int read_cal_coef(void);
+static int measure(int32_t * temperature, int32_t * pressure);
+
+/* libchardriver callbacks */
+static struct device *bmp085_prepare(dev_t UNUSED(dev));
+static int bmp085_transfer(endpoint_t endpt, int opcode, u64_t position,
+ iovec_t * iov, unsigned nr_req, endpoint_t UNUSED(user_endpt),
+ unsigned int UNUSED(flags));
+static int bmp085_other(message * m);
+
+/* i2c bus access */
+static int reg_read(uint8_t reg, uint8_t * val);
+static int reg_read16(uint8_t reg, uint16_t * val);
+static int reg_read24(uint8_t reg, uint32_t * val);
+static int reg_write(uint8_t reg, uint8_t val);
+
+/* SEF Function */
+static int sef_cb_lu_state_save(int);
+static int lu_state_restore(void);
+static int sef_cb_init(int type, sef_init_info_t * info);
+static void sef_local_startup(void);
+
+/* Entry points to this driver from libchardriver. */
+static struct chardriver bmp085_tab = {
+ .cdr_open = do_nop,
+ .cdr_close = do_nop,
+ .cdr_ioctl = nop_ioctl,
+ .cdr_prepare = bmp085_prepare,
+ .cdr_transfer = bmp085_transfer,
+ .cdr_cleanup = nop_cleanup,
+ .cdr_alarm = nop_alarm,
+ .cdr_cancel = nop_cancel,
+ .cdr_select = nop_select,
+ .cdr_other = bmp085_other
+};
+
+static struct device bmp085_device = {
+ .dv_base = 0,
+ .dv_size = 0
+};
+
+static int
+reg_read(uint8_t reg, uint8_t * val)
+{
+ int r;
+ minix_i2c_ioctl_exec_t ioctl_exec;
+
+ if (val == NULL) {
+ log_warn(&log, "reg_read() called with NULL pointer\n");
+ return -1;
+ }
+
+ memset(&ioctl_exec, '\0', sizeof(minix_i2c_ioctl_exec_t));
+
+ /* Read from chip */
+ ioctl_exec.iie_op = I2C_OP_READ_WITH_STOP;
+ ioctl_exec.iie_addr = address;
+
+ /* write the register address */
+ ioctl_exec.iie_cmd[0] = reg;
+ ioctl_exec.iie_cmdlen = 1;
+
+ /* read 1 byte */
+ ioctl_exec.iie_buflen = 1;
+
+ r = i2cdriver_exec(bus_endpoint, &ioctl_exec);
+ if (r != OK) {
+ log_warn(&log, "reg_read() failed (r=%d)\n", r);
+ return -1;
+ }
+
+ *val = ioctl_exec.iie_buf[0];
+
+ log_trace(&log, "Read 0x%x from reg 0x%x\n", *val, reg);
+
+ return OK;
+}
+
+static int
+reg_read16(uint8_t reg, uint16_t * val)
+{
+ int r;
+ uint8_t msb, lsb;
+ minix_i2c_ioctl_exec_t ioctl_exec;
+
+ if (val == NULL) {
+ log_warn(&log, "reg_read16() called with NULL pointer\n");
+ return -1;
+ }
+
+ memset(&ioctl_exec, '\0', sizeof(minix_i2c_ioctl_exec_t));
+
+ /* Read from chip */
+ ioctl_exec.iie_op = I2C_OP_READ_WITH_STOP;
+ ioctl_exec.iie_addr = address;
+
+ /* write the register address */
+ ioctl_exec.iie_cmd[0] = reg;
+ ioctl_exec.iie_cmdlen = 1;
+
+ /* read 2 bytes */
+ ioctl_exec.iie_buflen = 2;
+
+ r = i2cdriver_exec(bus_endpoint, &ioctl_exec);
+ if (r != OK) {
+ log_warn(&log, "reg_read16() failed (r=%d)\n", r);
+ return -1;
+ }
+
+ msb = ioctl_exec.iie_buf[0];
+ lsb = ioctl_exec.iie_buf[1];
+
+ *val = ((msb << 8) | lsb);
+
+ log_trace(&log, "Read 0x%x from reg 0x%x\n", *val, reg);
+
+ return OK;
+
+}
+
+static int
+reg_read24(uint8_t reg, uint32_t * val)
+{
+ int r;
+ uint8_t msb, lsb, xlsb;
+ minix_i2c_ioctl_exec_t ioctl_exec;
+
+ if (val == NULL) {
+ log_warn(&log, "reg_read24() called with NULL pointer\n");
+ return -1;
+ }
+
+ memset(&ioctl_exec, '\0', sizeof(minix_i2c_ioctl_exec_t));
+
+ /* Read from chip */
+ ioctl_exec.iie_op = I2C_OP_READ_WITH_STOP;
+ ioctl_exec.iie_addr = address;
+
+ /* write the register address */
+ ioctl_exec.iie_cmd[0] = reg;
+ ioctl_exec.iie_cmdlen = 1;
+
+ /* read 3 bytes */
+ ioctl_exec.iie_buflen = 3;
+
+ r = i2cdriver_exec(bus_endpoint, &ioctl_exec);
+ if (r != OK) {
+ log_warn(&log, "reg_read24() failed (r=%d)\n", r);
+ return -1;
+ }
+
+ msb = ioctl_exec.iie_buf[0];
+ lsb = ioctl_exec.iie_buf[1];
+ xlsb = ioctl_exec.iie_buf[2];
+
+ *val = ((msb << 16) | (lsb << 8) | xlsb);
+
+ log_trace(&log, "Read 0x%x from reg 0x%x\n", *val, reg);
+
+ return OK;
+
+}
+
+static int
+reg_write(uint8_t reg, uint8_t val)
+{
+ int r;
+ minix_i2c_ioctl_exec_t ioctl_exec;
+
+ memset(&ioctl_exec, '\0', sizeof(minix_i2c_ioctl_exec_t));
+
+ /* Read from chip */
+ ioctl_exec.iie_op = I2C_OP_WRITE_WITH_STOP;
+ ioctl_exec.iie_addr = address;
+
+ /* no commands here */
+ ioctl_exec.iie_cmdlen = 0;
+
+ /* write register and value */
+ ioctl_exec.iie_buf[0] = reg;
+ ioctl_exec.iie_buf[1] = val;
+ ioctl_exec.iie_buflen = 2;
+
+ r = i2cdriver_exec(bus_endpoint, &ioctl_exec);
+ if (r != OK) {
+ log_warn(&log, "reg_write() failed (r=%d)\n", r);
+ return -1;
+ }
+
+ log_trace(&log, "Wrote 0x%x to reg 0x%x\n", val, reg);
+
+ return OK;
+}
+
+/*
+ * Initialize the driver. Checks the CHIPID against a known value and
+ * reads the calibration coefficients.
+ *
+ * The chip does have a soft reset register (0xe0), but there
+ * doesn't appear to be any documentation or example usage for it.
+ */
+static int
+bmp085_init(void)
+{
+ int r;
+ int32_t t, p;
+
+ r = version_check();
+ if (r != OK) {
+ return EXIT_FAILURE;
+ }
+
+ r = read_cal_coef();
+ if (r != OK) {
+ return EXIT_FAILURE;
+ }
+
+ return OK;
+}
+
+static int
+version_check(void)
+{
+ int r;
+ uint8_t chipid;
+
+ r = reg_read(CHIPID_REG, &chipid);
+ if (r != OK) {
+ log_warn(&log, "Couldn't read CHIPID\n");
+ return -1;
+ }
+
+ if (chipid != BMP085_CHIPID) {
+ log_warn(&log, "Bad CHIPID\n");
+ return -1;
+ }
+
+ log_debug(&log, "CHIPID OK\n");
+
+ return OK;
+}
+
+/*
+ * Read the calibration data from the chip. Each individual chip has a unique
+ * set of calibration parameters that get used to compute the true temperature
+ * and pressure.
+ */
+static int
+read_cal_coef(void)
+{
+ int r;
+
+ /* Populate the calibration struct with values */
+ r = reg_read16(AC1_MSB_REG, &cal.ac1);
+ if (r != OK) {
+ return -1;
+ }
+ log_debug(&log, "cal.ac1 = %d\n", cal.ac1);
+
+ r = reg_read16(AC2_MSB_REG, &cal.ac2);
+ if (r != OK) {
+ return -1;
+ }
+ log_debug(&log, "cal.ac2 = %d\n", cal.ac2);
+
+ r = reg_read16(AC3_MSB_REG, &cal.ac3);
+ if (r != OK) {
+ return -1;
+ }
+ log_debug(&log, "cal.ac3 = %d\n", cal.ac3);
+
+ r = reg_read16(AC4_MSB_REG, &cal.ac4);
+ if (r != OK) {
+ return -1;
+ }
+ log_debug(&log, "cal.ac4 = %u\n", cal.ac4);
+
+ r = reg_read16(AC5_MSB_REG, &cal.ac5);
+ if (r != OK) {
+ return -1;
+ }
+ log_debug(&log, "cal.ac5 = %u\n", cal.ac5);
+
+ r = reg_read16(AC6_MSB_REG, &cal.ac6);
+ if (r != OK) {
+ return -1;
+ }
+ log_debug(&log, "cal.ac6 = %u\n", cal.ac6);
+
+ r = reg_read16(B1_MSB_REG, &cal.b1);
+ if (r != OK) {
+ return -1;
+ }
+ log_debug(&log, "cal.b1 = %d\n", cal.b1);
+
+ r = reg_read16(B2_MSB_REG, &cal.b2);
+ if (r != OK) {
+ return -1;
+ }
+ log_debug(&log, "cal.b2 = %d\n", cal.b2);
+
+ r = reg_read16(MB_MSB_REG, &cal.mb);
+ if (r != OK) {
+ return -1;
+ }
+ log_debug(&log, "cal.mb = %d\n", cal.mb);
+
+ r = reg_read16(MC_MSB_REG, &cal.mc);
+ if (r != OK) {
+ return -1;
+ }
+ log_debug(&log, "cal.mc = %d\n", cal.mc);
+
+ r = reg_read16(MD_MSB_REG, &cal.md);
+ if (r != OK) {
+ return -1;
+ }
+ log_debug(&log, "cal.md = %d\n", cal.md);
+
+ /* Validate. Data sheet says values should not be 0x0000 nor 0xffff */
+ if (!CAL_COEF_IS_VALID(cal.ac1) ||
+ !CAL_COEF_IS_VALID(cal.ac2) ||
+ !CAL_COEF_IS_VALID(cal.ac3) ||
+ !CAL_COEF_IS_VALID(cal.ac4) ||
+ !CAL_COEF_IS_VALID(cal.ac5) ||
+ !CAL_COEF_IS_VALID(cal.ac6) ||
+ !CAL_COEF_IS_VALID(cal.b1) ||
+ !CAL_COEF_IS_VALID(cal.b2) ||
+ !CAL_COEF_IS_VALID(cal.mb) ||
+ !CAL_COEF_IS_VALID(cal.mc) || !CAL_COEF_IS_VALID(cal.md)) {
+
+ log_warn(&log, "Invalid calibration data found on chip.\n");
+ return -1;
+ }
+
+ log_debug(&log, "Read Cal Data OK\n");
+
+ return OK;
+}
+
+/*
+ * Measure the uncompensated temperature and uncompensated pressure from the
+ * chip and apply the formulas to determine the true temperature and pressure.
+ * Note, the data sheet is light on the details when it comes to defining the
+ * meaning of each variable, so this function has a lot of cryptic names in it.
+ */
+static int
+measure(int32_t * temperature, int32_t * pressure)
+{
+ int r;
+
+ /* Types are given in the datasheet. Their long translates to 32-bits */
+
+ int16_t ut; /* uncompensated temperature */
+ int32_t up; /* uncompensated pressure */
+ int32_t x1;
+ int32_t x2;
+ int32_t x3;
+ int32_t b3;
+ uint32_t b4;
+ int32_t b5;
+ int32_t b6;
+ uint32_t b7;
+ int32_t t; /* true temperature (in 0.1C) */
+ int32_t p; /* true pressure (in Pa) */
+
+ log_debug(&log, "Triggering Temp Reading...\n");
+
+ /* trigger temperature reading */
+ r = reg_write(CTRL_REG, CMD_TRIG_T);
+ if (r != OK) {
+ log_warn(&log, "Failed to trigger temperature reading.\n");
+ return -1;
+ }
+ micro_delay(UDELAY_T);
+
+ /* trigger temperature reading */
+ r = reg_write(CTRL_REG, CMD_TRIG_T);
+ if (r != OK) {
+ log_warn(&log, "Failed to trigger temperature reading.\n");
+ return -1;
+ }
+
+ /* wait for sampling to be completed. */
+ micro_delay(UDELAY_T);
+
+ /* read the uncompensated temperature */
+ r = reg_read16(SENSOR_VAL_MSB_REG, &ut);
+ if (r != OK) {
+ log_warn(&log, "Failed to read temperature.\n");
+ return -1;
+ }
+
+ log_debug(&log, "ut = %d\n", ut);
+
+ log_debug(&log, "Triggering Pressure Reading...\n");
+
+ /* trigger pressure reading */
+ r = reg_write(CTRL_REG, p_cmd->cmd);
+ if (r != OK) {
+ log_warn(&log, "Failed to trigger pressure reading.\n");
+ return -1;
+ }
+
+ /* wait for sampling to be completed. */
+ micro_delay(p_cmd->udelay);
+
+ /* read the uncompensated pressure */
+ r = reg_read24(SENSOR_VAL_MSB_REG, &up);
+ if (r != OK) {
+ log_warn(&log, "Failed to read pressure.\n");
+ return -1;
+ }
+
+ /* shift by 8 - oversampling setting */
+ up = (up >> (8 - p_cmd->mode));
+
+ log_debug(&log, "up = %d\n", up);
+
+ /* convert uncompensated temperature to true temperature */
+ x1 = ((ut - cal.ac6) * cal.ac5) / (1 << 15);
+ x2 = (cal.mc * (1 << 11)) / (x1 + cal.md);
+ b5 = x1 + x2;
+ t = (b5 + 8) / (1 << 4);
+
+ /* save the result */
+ *temperature = t;
+
+ log_debug(&log, "t = %d\n", t);
+
+ /* Convert uncompensated pressure to true pressure.
+ * This is really how the data sheet suggests doing it.
+ * There is no alternative approach suggested. Other open
+ * source drivers I've found use this method.
+ */
+ b6 = b5 - 4000;
+ x1 = ((cal.b2 * ((b6 * b6) >> 12)) >> 11);
+ x2 = ((cal.ac2 * b6) >> 11);
+ x3 = x1 + x2;
+ b3 = (((((cal.ac1 * 4) + x3) << p_cmd->mode) + 2) >> 2);
+ x1 = ((cal.ac3 * b6) >> 13);
+ x2 = ((cal.b1 * ((b6 * b6) >> 12)) >> 16);
+ x3 = (((x1 + x2) + 2) >> 2);
+ b4 = ((cal.ac4 * ((uint32_t) (x3 + 32768))) >> 15);
+ b7 = ((uint32_t) up - b3) * (50000 >> p_cmd->mode);
+ p = (b7 < 0x80000000) ? (b7 * 2) / b4 : (b7 / b4) * 2;
+ x1 = (p >> 8) * (p >> 8);
+ x1 = ((x1 * 3038) >> 16);
+ x2 = ((-7357 * p) >> 16);
+ p = p + ((x1 + x2 + 3791) >> 4);
+
+ *pressure = p;
+
+ log_debug(&log, "p = %d\n", p);
+
+ return OK;
+}
+
+static struct device *
+bmp085_prepare(dev_t UNUSED(dev))
+{
+ return &bmp085_device;
+}
+
+static int
+bmp085_transfer(endpoint_t endpt, int opcode, u64_t position,
+ iovec_t * iov, unsigned nr_req, endpoint_t UNUSED(user_endpt),
+ unsigned int UNUSED(flags))
+{
+ int bytes, r;
+ uint32_t temperature, pressure;
+
+ r = measure(&temperature, &pressure);
+ if (r != OK) {
+ return EIO;
+ }
+
+ memset(buffer, '\0', BUFFER_LEN + 1);
+ snprintf(buffer, BUFFER_LEN, "%-16s: %d.%01d\n%-16s: %d\n",
+ "TEMPERATURE", temperature / 10, temperature % 10, "PRESSURE",
+ pressure);
+
+ log_trace(&log, "%s", buffer);
+
+ bytes = strlen(buffer) - position < iov->iov_size ?
+ strlen(buffer) - position : iov->iov_size;
+
+ if (bytes <= 0) {
+ return OK;
+ }
+
+ switch (opcode) {
+ case DEV_GATHER_S:
+ r = sys_safecopyto(endpt, (cp_grant_id_t) iov->iov_addr, 0,
+ (vir_bytes) (buffer + position), bytes);
+ iov->iov_size -= bytes;
+ break;
+ default:
+ return EINVAL;
+ }
+
+ return r;
+}
+
+static int
+bmp085_other(message * m)
+{
+ int r;
+
+ switch (m->m_type) {
+ case NOTIFY_MESSAGE:
+ if (m->m_source == DS_PROC_NR) {
+ log_debug(&log,
+ "bus driver changed state, update endpoint\n");
+ i2cdriver_handle_bus_update(&bus_endpoint, bus,
+ address);
+ }
+ r = OK;
+ break;
+ default:
+ log_warn(&log, "Invalid message type (0x%x)\n", m->m_type);
+ r = EINVAL;
+ break;
+ }
+
+ return r;
+}
+
+static int
+sef_cb_lu_state_save(int UNUSED(state))
+{
+ ds_publish_u32("bus", bus, DSF_OVERWRITE);
+ ds_publish_u32("address", address, DSF_OVERWRITE);
+ return OK;
+}
+
+static int
+lu_state_restore(void)
+{
+ /* Restore the state. */
+ u32_t value;
+
+ ds_retrieve_u32("bus", &value);
+ ds_delete_u32("bus");
+ bus = (int) value;
+
+ ds_retrieve_u32("address", &value);
+ ds_delete_u32("address");
+ address = (int) value;
+
+ return OK;
+}
+
+static int
+sef_cb_init(int type, sef_init_info_t * UNUSED(info))
+{
+ int r;
+
+ if (type == SEF_INIT_LU) {
+ /* Restore the state. */
+ lu_state_restore();
+ }
+
+ /* look-up the endpoint for the bus driver */
+ bus_endpoint = i2cdriver_bus_endpoint(bus);
+ if (bus_endpoint == 0) {
+ log_warn(&log, "Couldn't find bus driver.\n");
+ return EXIT_FAILURE;
+ }
+
+ /* claim the device */
+ r = i2cdriver_reserve_device(bus_endpoint, address);
+ if (r != OK) {
+ log_warn(&log, "Couldn't reserve device 0x%x (r=%d)\n",
+ address, r);
+ return EXIT_FAILURE;
+ }
+
+ r = bmp085_init();
+ if (r != OK) {
+ log_warn(&log, "Couldn't initialize device\n");
+ return EXIT_FAILURE;
+ }
+
+ if (type != SEF_INIT_LU) {
+
+ /* sign up for updates about the i2c bus going down/up */
+ r = i2cdriver_subscribe_bus_updates(bus);
+ if (r != OK) {
+ log_warn(&log, "Couldn't subscribe to bus updates\n");
+ return EXIT_FAILURE;
+ }
+
+ i2cdriver_announce(bus);
+ log_debug(&log, "announced\n");
+ }
+
+ return OK;
+}
+
+static void
+sef_local_startup(void)
+{
+ /*
+ * Register init callbacks. Use the same function for all event types
+ */
+ sef_setcb_init_fresh(sef_cb_init);
+ sef_setcb_init_lu(sef_cb_init);
+ sef_setcb_init_restart(sef_cb_init);
+
+ /*
+ * Register live update callbacks.
+ */
+ /* Agree to update immediately when LU is requested in a valid state. */
+ sef_setcb_lu_prepare(sef_cb_lu_prepare_always_ready);
+ /* Support live update starting from any standard state. */
+ sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid_standard);
+ /* Register a custom routine to save the state. */
+ sef_setcb_lu_state_save(sef_cb_lu_state_save);
+
+ /* Let SEF perform startup. */
+ sef_startup();
+}
+
+int
+main(int argc, char *argv[])
+{
+ int r;
+
+ env_setargs(argc, argv);
+
+ r = i2cdriver_env_parse(&bus, &address, valid_addrs);
+ if (r < 0) {
+ log_warn(&log, "Expecting -args 'bus=X address=0x77'\n");
+ log_warn(&log, "Example -args 'bus=1 address=0x77'\n");
+ return EXIT_FAILURE;
+ } else if (r > 0) {
+ log_warn(&log,
+ "Invalid slave address for device, expecting 0x77\n");
+ return EXIT_FAILURE;
+ }
+
+ sef_local_startup();
+
+ chardriver_task(&bmp085_tab, CHARDRIVER_SYNC);
+
+ return 0;
+}