diff options
author | Christian Mauderer <christian.mauderer@embedded-brains.de> | 2021-05-26 16:33:40 +0200 |
---|---|---|
committer | Christian Mauderer <christian.mauderer@embedded-brains.de> | 2021-06-22 13:51:17 +0200 |
commit | 5bb5e013563ed6825aa5ca0c93e1b01db7e721cd (patch) | |
tree | d5aa3c9ee5e8aa05141fa03ca7c9155954a5827e /cpukit | |
parent | cpukit: Add timespecisnonnegative to Makefile.am (diff) | |
download | rtems-5bb5e013563ed6825aa5ca0c93e1b01db7e721cd.tar.bz2 |
i2c: Add non blocking read / write
This adds the possibility to open an I2C bus with O_NONBLOCK (or set it
later via fcntl) to get non-blocking transmissions. This means that if
the bus is busy, a read, write or transfer ioctl will return with a
EAGAIN errno.
Diffstat (limited to '')
-rw-r--r-- | cpukit/dev/i2c/i2c-bus.c | 45 | ||||
-rw-r--r-- | cpukit/include/dev/i2c/i2c.h | 44 |
2 files changed, 83 insertions, 6 deletions
diff --git a/cpukit/dev/i2c/i2c-bus.c b/cpukit/dev/i2c/i2c-bus.c index 472222c4ab..618a817b1a 100644 --- a/cpukit/dev/i2c/i2c-bus.c +++ b/cpukit/dev/i2c/i2c-bus.c @@ -31,6 +31,11 @@ #include <stdlib.h> #include <string.h> +int i2c_bus_try_obtain(i2c_bus *bus) +{ + return rtems_recursive_mutex_try_lock(&bus->mutex); +} + void i2c_bus_obtain(i2c_bus *bus) { rtems_recursive_mutex_lock(&bus->mutex); @@ -41,7 +46,12 @@ void i2c_bus_release(i2c_bus *bus) rtems_recursive_mutex_unlock(&bus->mutex); } -int i2c_bus_transfer(i2c_bus *bus, i2c_msg *msgs, uint32_t msg_count) +int i2c_bus_do_transfer( + i2c_bus *bus, + i2c_msg *msgs, + uint32_t msg_count, + uint32_t flags +) { int err; uint32_t i; @@ -63,13 +73,24 @@ int i2c_bus_transfer(i2c_bus *bus, i2c_msg *msgs, uint32_t msg_count) } } - i2c_bus_obtain(bus); + if ((flags & I2C_BUS_NOBLOCK) != 0) { + if (i2c_bus_try_obtain(bus) != 0) { + return -EAGAIN; + } + } else { + i2c_bus_obtain(bus); + } err = (*bus->transfer)(bus, msgs, msg_count); i2c_bus_release(bus); return err; } +int i2c_bus_transfer(i2c_bus *bus, i2c_msg *msgs, uint32_t msg_count) +{ + return i2c_bus_do_transfer(bus, msgs, msg_count, 0); +} + static ssize_t i2c_bus_read( rtems_libio_t *iop, void *buffer, @@ -84,12 +105,17 @@ static ssize_t i2c_bus_read( .buf = buffer }; int err; + unsigned flags = 0; if (bus->ten_bit_address) { msg.flags |= I2C_M_TEN; } - err = i2c_bus_transfer(bus, &msg, 1); + if (rtems_libio_iop_is_no_delay(iop)) { + flags |= I2C_BUS_NOBLOCK; + } + + err = i2c_bus_do_transfer(bus, &msg, 1, flags); if (err == 0) { return msg.len; } else { @@ -111,12 +137,17 @@ static ssize_t i2c_bus_write( .buf = RTEMS_DECONST(void *, buffer) }; int err; + unsigned flags = 0; if (bus->ten_bit_address) { msg.flags |= I2C_M_TEN; } - err = i2c_bus_transfer(bus, &msg, 1); + if (rtems_libio_iop_is_no_delay(iop)) { + flags |= I2C_BUS_NOBLOCK; + } + + err = i2c_bus_do_transfer(bus, &msg, 1, flags); if (err == 0) { return msg.len; } else { @@ -133,12 +164,16 @@ static int i2c_bus_ioctl( i2c_bus *bus = IMFS_generic_get_context_by_iop(iop); i2c_rdwr_ioctl_data *rdwr; int err; + unsigned flags = 0; switch (command) { case I2C_RDWR: rdwr = arg; if (rdwr->nmsgs > 0) { - err = i2c_bus_transfer(bus, rdwr->msgs, rdwr->nmsgs); + if (rtems_libio_iop_is_no_delay(iop)) { + flags |= I2C_BUS_NOBLOCK; + } + err = i2c_bus_do_transfer(bus, rdwr->msgs, rdwr->nmsgs, flags); } else { err = 0; } diff --git a/cpukit/include/dev/i2c/i2c.h b/cpukit/include/dev/i2c/i2c.h index ac2c369785..5aa45e390c 100644 --- a/cpukit/include/dev/i2c/i2c.h +++ b/cpukit/include/dev/i2c/i2c.h @@ -243,6 +243,16 @@ int i2c_bus_register( ); /** + * @brief Try to obtain the bus. + * + * @param[in] bus The bus control. + * + * @retval 0 Successful operation. + * @retval EBUSY if mutex is already locked. + */ +int i2c_bus_try_obtain(i2c_bus *bus); + +/** * @brief Obtains the bus. * * @param[in] bus The bus control. @@ -259,7 +269,8 @@ void i2c_bus_release(i2c_bus *bus); /** * @brief Transfers I2C messages. * - * The bus is obtained before the transfer and released afterwards. + * The bus is obtained before the transfer and released afterwards. This is the + * same like calling @ref i2c_bus_do_transfer with flags set to 0. * * @param[in] bus The bus control. * @param[in] msgs The messages to transfer. @@ -271,6 +282,37 @@ void i2c_bus_release(i2c_bus *bus); */ int i2c_bus_transfer(i2c_bus *bus, i2c_msg *msgs, uint32_t msg_count); +/** + * @brief Transfers I2C messages with optional flags. + * + * The bus is obtained before the transfer and released afterwards. If the flag + * I2C_BUS_NOBLOCK is set and the bus is already obtained, nothing will be + * transfered and the function returns with an -EAGAIN. + * + * @param[in] bus The bus control. + * @param[in] msgs The messages to transfer. + * @param[in] msg_count The count of messages to transfer. It must be + * positive. + * @param[in] flags Options for the whole transfer. + * + * @retval 0 Successful operation. + * @retval -EAGAIN if @ref I2C_BUS_NOBLOCK is set and the bus is already + * obtained. + * @retval negative Negative error number in case of an error. + */ +int i2c_bus_do_transfer( + i2c_bus *bus, + i2c_msg *msgs, + uint32_t msg_count, + uint32_t flags +); + +/** + * @brief I2C bus transfer flag to indicate that the task should not block if + * the bus is busy on a new transfer. + */ +#define I2C_BUS_NOBLOCK (1u << 0) + /** @} */ /** |