From 471dc65ada2ab65eee09cbe7dca36dd06ea01b2b Mon Sep 17 00:00:00 2001 From: Thomas Cort Date: Tue, 20 Aug 2013 10:43:51 -0400 Subject: [PATCH] bmp085: driver for the BMP085 temp&pressure sensor Change-Id: I5c297a7f4f8cea2549e537df30a5c7bf5b9d8b51 --- commands/MAKEDEV/MAKEDEV.sh | 11 +- distrib/sets/lists/minix/md.evbarm | 1 + drivers/Makefile | 2 +- drivers/bmp085/Makefile | 14 + drivers/bmp085/README.txt | 63 +++ drivers/bmp085/bmp085.c | 797 +++++++++++++++++++++++++++++ etc/system.conf | 5 + include/minix/dmap.h | 3 + 8 files changed, 893 insertions(+), 3 deletions(-) create mode 100644 drivers/bmp085/Makefile create mode 100644 drivers/bmp085/README.txt create mode 100644 drivers/bmp085/bmp085.c diff --git a/commands/MAKEDEV/MAKEDEV.sh b/commands/MAKEDEV/MAKEDEV.sh index df4f9a0e4..9a4c1a9cd 100644 --- a/commands/MAKEDEV/MAKEDEV.sh +++ b/commands/MAKEDEV/MAKEDEV.sh @@ -32,7 +32,8 @@ case $#:$1 in eepromb3s50 eepromb3s51 eepromb3s52 eepromb3s53 \ eepromb3s54 eepromb3s55 eepromb3s56 eepromb3s57 \ tsl2550b1s39 tsl2550b2s39 tsl2550b3s39 \ - sht21b1s40 sht21b2s40 sht21b3s40 + sht21b1s40 sht21b2s40 sht21b3s40 \ + bmp085b1s77 bmp085b2s77 bmp085b3s77 ;; 0:|1:-\?) cat >&2 <&2 ex=1 diff --git a/distrib/sets/lists/minix/md.evbarm b/distrib/sets/lists/minix/md.evbarm index d301ca958..95360b4cb 100644 --- a/distrib/sets/lists/minix/md.evbarm +++ b/distrib/sets/lists/minix/md.evbarm @@ -105,6 +105,7 @@ ./usr/lib/libpadconf_pic.a minix-sys ./usr/man/man1/eepromread.1 minix-sys ./usr/mdec minix-sys +./usr/sbin/bmp085 minix-sys ./usr/sbin/cat24c256 minix-sys ./usr/sbin/fb minix-sys ./usr/sbin/gpio minix-sys diff --git a/drivers/Makefile b/drivers/Makefile index 05c8c131b..907ad6d9d 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -23,7 +23,7 @@ SUBDIR= ahci amddev atl2 at_wini audio dec21140A dp8390 dpeth \ .endif .if ${MACHINE_ARCH} == "earm" -SUBDIR= cat24c256 fb gpio i2c mmc lan8710a log readclock \ +SUBDIR= bmp085 cat24c256 fb gpio i2c mmc lan8710a log readclock \ sht21 tda19988 tps65217 tps65950 tsl2550 tty random .endif diff --git a/drivers/bmp085/Makefile b/drivers/bmp085/Makefile new file mode 100644 index 000000000..e087328dc --- /dev/null +++ b/drivers/bmp085/Makefile @@ -0,0 +1,14 @@ +# Makefile for the bmp085 pressure and temp sensor found on the Weather Cape. +PROG= bmp085 +SRCS= bmp085.c + +DPADD+= ${LIBI2CDRIVER} ${LIBCHARDRIVER} ${LIBSYS} ${LIBTIMERS} +LDADD+= -li2cdriver -lchardriver -lsys -ltimers + +MAN= + +BINDIR?= /usr/sbin + +CPPFLAGS+= -I${NETBSDSRCDIR} + +.include diff --git a/drivers/bmp085/README.txt b/drivers/bmp085/README.txt new file mode 100644 index 000000000..d6d0cc317 --- /dev/null +++ b/drivers/bmp085/README.txt @@ -0,0 +1,63 @@ +BMP085 Driver (Pressure and Temperature Sensor) +=============================================== + +Overview +-------- + +This is the driver for the pressure and temperature sensor commonly found on +the WeatherCape expansion board for the BeagleBone. + +Interface +--------- + +This driver implements the character device interface. It supports reading +through /dev/bmp085b{1,3}s77. When read from, it returns a string containing +a data label, a colon, and the sensor value. + +Example output of `cat /dev/bmp085b3s77`: + +TEMPERATURE : 23.1 +PRESSURE : 69964 + +Temperature is expressed in Celsius (a.k.a. centigrade). The resolution is +0.1C. + +Pressure is expressed in Pascals. Valid values are 30000 to 110000. +The resolution is 3 Pa. + +Limitations +----------- + +The measurement resolution is configurable in the chip, but this driver just +uses standard mode. It could probably be implemented with an ioctl() or by +passing an argument via the service command, but it doesn't seem too useful at +this time. See the data sheet for the trade-offs between conversion time, +power consumption, and resolution. + +While only the BMP085 is supported at present, the BMP085's predecessor, +SMD500, should be easy to support in this driver with some small changes +to the calculations and coefficients. + +As with the SHT21's temperature sensor, the BMP085's temperature sensor +appears to be heated a couple of degrees by the BeagleBone. After power-on, +the readings rise slightly and then level off a few degrees above room +temperature. + +Testing the Code +---------------- + +The driver should have been started by a script in /etc/rc.capes/ If not, +this is how you start up an instance: + +cd /dev && MAKEDEV bmp085b3s77 +/bin/service up /usr/sbin/bmp085 -label bmp085.3.77 -dev /dev/bmp085b3s77 \ + -args 'bus=3 address=0x77' + +Getting the sensor value: + +cat /dev/bmp085b3s77 + +Killing an instance: + +/bin/service down bmp085.3.77 + diff --git a/drivers/bmp085/bmp085.c b/drivers/bmp085/bmp085.c new file mode 100644 index 000000000..40cd0aaba --- /dev/null +++ b/drivers/bmp085/bmp085.c @@ -0,0 +1,797 @@ +/* Driver for the BMP085 Preassure and Temperature Sensor */ + +#include +#include +#include +#include +#include +#include + +/* 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; +} diff --git a/etc/system.conf b/etc/system.conf index e46d183eb..aea6cb6c4 100644 --- a/etc/system.conf +++ b/etc/system.conf @@ -646,6 +646,11 @@ service sht21 ipc SYSTEM RS DS i2c; }; +service bmp085 +{ + ipc SYSTEM RS DS i2c; +}; + service vbox { system diff --git a/include/minix/dmap.h b/include/minix/dmap.h index 9a1c2f5a3..b041ee3e7 100644 --- a/include/minix/dmap.h +++ b/include/minix/dmap.h @@ -72,6 +72,9 @@ enum dev_style { STYLE_NDEV, STYLE_DEV, STYLE_DEVA, STYLE_TTY, STYLE_CTTY, #define SHT21B1S40_MAJOR 50 /* 50 = /dev/sht21b1s40 (sht21) */ #define SHT21B2S40_MAJOR 51 /* 51 = /dev/sht21b2s40 (sht21) */ #define SHT21B3S40_MAJOR 52 /* 52 = /dev/sht21b3s40 (sht21) */ +#define BMP085B1S77_MAJOR 53 /* 53 = /dev/bmp085b1s77 (bmp085) */ +#define BMP085B2S77_MAJOR 54 /* 54 = /dev/bmp085b2s77 (bmp085) */ +#define BMP085B3S77_MAJOR 55 /* 55 = /dev/bmp085b3s77 (bmp085) */ /* Minor device numbers for memory driver. */ -- 2.44.0