From: Cyprien Laplace Date: Sat, 17 Jun 2017 14:58:22 +0000 (-0400) Subject: i2c: add asynchronous write X-Git-Url: http://git.techno-innov.fr/?a=commitdiff_plain;h=8cefe33dc08dcaadbfb5d4641094dc7e77202121;p=soft%2Flpc122x%2Fcore i2c: add asynchronous write i2c_read() and i2c_write() are blocking functions, and their comments have been updated to reflect this. The new i2c_write_async() function is starting an i2c transfer, and returns immediatly. All i2c transfer functions will now return -EAGAIN if there is already a transfer in progress (-EBUSY was conflicting with arbitration lost). ssd130x_display_full_screen() has been updated to use this new asynchronous write to start the transfer of the full screen. All i2c_write* calls are placed in a retry loop if the call returns -EAGAIN. --- diff --git a/drivers/i2c.c b/drivers/i2c.c index 0d96e21..1812fc9 100644 --- a/drivers/i2c.c +++ b/drivers/i2c.c @@ -274,26 +274,25 @@ void I2C_0_Handler(void) } - /***************************************************************************** */ /* I2C access */ /***************************************************************************** */ -static int i2c_perform_data_transfer(struct i2c_bus* i2c) + +/* Translate I2C state to 0 on success or a glibc error code. + */ +static int i2c_state(struct i2c_bus* i2c) { int ret = 0; - /* Start the process */ - i2c->state = I2C_BUSY; - i2c->regs->ctrl_set = I2C_START_FLAG; - /* Wait for process completion */ - do {} while (i2c->state == I2C_BUSY); - /* Handle returned state (errors or OK) */ switch (i2c->state) { case I2C_OK: case I2C_NO_DATA: /* Return 0 : success */ break; + case I2C_BUSY: + ret = -EAGAIN; + break; case I2C_NACK: ret = -EREMOTEIO; break; @@ -310,6 +309,7 @@ static int i2c_perform_data_transfer(struct i2c_bus* i2c) return ret; } + /* Release Bus * Some devices do not release the Bus at the end of a transaction if they don't receive * a start condition immediately followed by a stop condition. @@ -328,7 +328,7 @@ void i2c_release_bus(uint8_t bus_num) /* Read - * Performs a non-blocking read on the module's i2c bus. + * Performs a blocking read on the module's i2c bus. * cmd_buf : buffer containing all control byte to be sent on the i2c bus * cmd_size : size of the cmd_buf command buffer * ctrl_buf : buffer containing action to be done after sending, like repeated START conditions @@ -353,12 +353,14 @@ int i2c_read(uint8_t bus_num, const void *cmd_buf, size_t cmd_size, const void* return -EINVAL; if (i2c->state == I2C_BUSY) { - return -EBUSY; + return -EAGAIN; } if (i2c->state != I2C_OK) { /* What should we do ??? someone failed to reset status ? */ } + i2c->state = I2C_BUSY; + /* Set up i2c structure for read operation */ /* command (write) buffer */ i2c->out_buff = cmd_buf; @@ -373,7 +375,12 @@ int i2c_read(uint8_t bus_num, const void *cmd_buf, size_t cmd_size, const void* i2c->read_length = count; i2c->read_index = 0; - ret = i2c_perform_data_transfer(i2c); + /* Start the process */ + i2c->regs->ctrl_set = I2C_START_FLAG; + /* Wait for process completion */ + do {} while (i2c->state == I2C_BUSY); + + ret = i2c_state(i2c); if (ret == 0) { return i2c->read_index; } @@ -381,7 +388,7 @@ int i2c_read(uint8_t bus_num, const void *cmd_buf, size_t cmd_size, const void* return ret; } -/* Write +/* Asynchronous Write * Performs a non-blocking write on the module's i2c bus. * buf : buffer containing all byte to be sent on the i2c bus, * including conrtol bytes (address, offsets, ...) @@ -390,13 +397,12 @@ int i2c_read(uint8_t bus_num, const void *cmd_buf, size_t cmd_size, const void* * FIXME : note that STOP + START conditions are not allowed, the STOP would lead to sending * the first bytes of buf, creating an infinite loop. * RETURN VALUE - * Upon successfull completion, returns the number of bytes written. On error, returns a negative + * Upon successfull transmition start, returns 0. On error, returns a negative * integer equivalent to errors from glibc. */ -int i2c_write(uint8_t bus_num, const void *buf, size_t count, const void* ctrl_buf) +int i2c_write_async(uint8_t bus_num, const void *buf, size_t count, const void* ctrl_buf) { struct i2c_bus* i2c = &(i2c_buses[0]); - int ret = 0; /* Checks */ if (i2c->regs != LPC_I2C0) @@ -405,16 +411,17 @@ int i2c_write(uint8_t bus_num, const void *buf, size_t count, const void* ctrl_b return -EINVAL; if (i2c->state == I2C_BUSY) { - return -EBUSY; + return -EAGAIN; } if (i2c->state != I2C_OK) { /* What should we do ??? someone failed to reset status ? */ } + i2c->state = I2C_BUSY; + /* Clear read information to prevent entering master receiver states */ i2c->read_length = 0; i2c->in_buff = NULL; - i2c->state = I2C_BUSY; /* Set up i2c_bus structure for write operation */ i2c->out_buff = buf; i2c->write_length = count; @@ -424,7 +431,39 @@ int i2c_write(uint8_t bus_num, const void *buf, size_t count, const void* ctrl_b i2c->repeated_start_restart = ctrl_buf; i2c->restart_after_data = 0; - ret = i2c_perform_data_transfer(i2c); + /* Start the process */ + i2c->regs->ctrl_set = I2C_START_FLAG; + + return 0; +} + + +/* Write + * Performs a blocking write on the module's i2c bus. + * buf : buffer containing all byte to be sent on the i2c bus, + * including conrtol bytes (address, offsets, ...) + * count : the number of bytes to be sent, including address bytes and so on. + * ctrl_buf : buffer containing action to be done after sending, like repeated START conditions + * FIXME : note that STOP + START conditions are not allowed, the STOP would lead to sending + * the first bytes of buf, creating an infinite loop. + * RETURN VALUE + * Upon successfull completion, returns the number of bytes written. On error, returns a negative + * integer equivalent to errors from glibc. + */ +int i2c_write(uint8_t bus_num, const void *buf, size_t count, const void* ctrl_buf) +{ + struct i2c_bus* i2c = &(i2c_buses[0]); + int ret; + + ret = i2c_write_async(bus_num, buf, count, ctrl_buf); + if (ret != 0) { + return ret; + } + + /* Wait for process completion */ + do {} while (i2c->state == I2C_BUSY); + + ret = i2c_state(i2c); if (ret == 0) { return i2c->write_length; } diff --git a/extdrv/ssd130x_oled_driver.c b/extdrv/ssd130x_oled_driver.c index 8f8355b..d7c1806 100644 --- a/extdrv/ssd130x_oled_driver.c +++ b/extdrv/ssd130x_oled_driver.c @@ -103,7 +103,9 @@ int ssd130x_send_command(struct oled_display* conf, uint8_t cmd, uint8_t* data, cmd_buf[ 4 + (2 * i) ] = data[i]; } } - ret = i2c_write(conf->bus_num, cmd_buf, (3 + (len * 2)), NULL); + do { + ret = i2c_write(conf->bus_num, cmd_buf, (3 + (len * 2)), NULL); + } while (ret == -EAGAIN); if (ret != (3 + (len * 2))) { conf->probe_ok = 0; return ret; @@ -330,7 +332,9 @@ int ssd130x_send_data(struct oled_display* conf, uint8_t* start, uint16_t len) *(start - 1) = SSD130x_DATA_ONLY; /* Send data on I2C bus */ - ret = i2c_write(conf->bus_num, (start - 2), (2 + len), NULL); + do { + ret = i2c_write(conf->bus_num, (start - 2), (2 + len), NULL); + } while (ret == -EAGAIN); /* Restore gddram data */ *(start - 2) = conf->gddram[0]; @@ -355,11 +359,17 @@ int ssd130x_display_full_screen(struct oled_display* conf) if (ret != 0) { return ret; } - ret = ssd130x_send_data(conf, conf->gddram + 4, GDDRAM_SIZE); - if (ret != GDDRAM_SIZE) { - return ret; - } - return 0; + + /* Setup I2C transfer */ + *(conf->gddram + 2) = conf->address; + *(conf->gddram + 3) = SSD130x_DATA_ONLY; + + /* Send data on I2C bus */ + do { + ret = i2c_write_async(conf->bus_num, conf->gddram + 2, 2 + GDDRAM_SIZE, NULL); + } while (ret == -EAGAIN); + + return ret; } /* Change a "tile" in the GDDRAM memory. diff --git a/include/drivers/i2c.h b/include/drivers/i2c.h index ecce01c..19a4a0e 100644 --- a/include/drivers/i2c.h +++ b/include/drivers/i2c.h @@ -104,7 +104,7 @@ enum i2c_state_machine_states { /* Modules I2C access */ /***************************************************************************** */ /* I2C Read - * Performs a non-blocking read on the i2c bus. + * Performs a blocking read on the i2c bus. * cmd_buf : buffer containing all control byte to be sent on the i2c bus * cmd_size : size of the cmd_buf command buffer * ctrl_buf : buffer containing action to be done after sending, like repeated START conditions @@ -124,7 +124,7 @@ enum i2c_state_machine_states { int i2c_read(uint8_t bus_num, const void *cmd_buf, size_t cmd_size, const void* ctrl_buf, void* inbuff, size_t count); /* I2C Write - * Performs a non-blocking write on the i2c bus. + * Performs a blocking write on the i2c bus. * buf : buffer containing all byte to be sent on the i2c bus, * including conrtol bytes (address, offsets, ...) * count : the number of bytes to be sent, including address bytes and so on. @@ -136,7 +136,8 @@ int i2c_read(uint8_t bus_num, const void *cmd_buf, size_t cmd_size, const void* * Upon successfull completion, returns the number of bytes written. On error, returns a negative * integer equivalent to errors from glibc. * -EBADFD : Device not initialized - * -EBUSY : Device or ressource Busy or Arbitration lost + * -EAGAIN : Device already in use + * -EBUSY : Arbitration lost * -EAGAIN : Device already in use * -EINVAL : Invalid argument (buf) * -EREMOTEIO : Device did not acknowledge @@ -144,6 +145,24 @@ int i2c_read(uint8_t bus_num, const void *cmd_buf, size_t cmd_size, const void* */ int i2c_write(uint8_t bus_num, const void *buf, size_t count, const void* ctrl_buf); +/* I2C Asynchronous Write + * Performs a non-blocking write on the i2c bus. + * buf : buffer containing all byte to be sent on the i2c bus, + * including conrtol bytes (address, offsets, ...) + * count : the number of bytes to be sent, including address bytes and so on. + * ctrl_buf : buffer containing action to be done after sending, like repeated START conditions + * ctrl_buf has the same size as cmd_buf + * FIXME : note that STOP + START conditions are not allowed, the STOP would lead to sending + * the first bytes of buf, creating an infinite loop. + * RETURN VALUE + * Upon successfull transmition start, returns 0. On error, returns a negative + * integer equivalent to errors from glibc. + * -EBADFD : Device not initialized + * -EAGAIN : Device already in use + * -EINVAL : Invalid argument (buf) + */ +int i2c_write_async(uint8_t bus_num, const void *buf, size_t count, const void* ctrl_buf); + /* Release Bus * Some devices do not release the Bus at the end of a transaction if they don't receive