From: minyard@acm.org
To: qemu-devel@nongnu.org,
"Dr . David Alan Gilbert" <dgilbert@redhat.com>,
"Philippe Mathieu-Daudé" <philmd@redhat.com>,
"Peter Maydell" <peter.maydell@linaro.org>
Cc: Paolo Bonzini <pbonzini@redhat.com>,
"Michael S . Tsirkin" <mst@redhat.com>,
Corey Minyard <minyard@acm.org>,
Corey Minyard <cminyard@mvista.com>
Subject: [Qemu-devel] [PATCH v3 05/16] i2c: Simplify and correct the SMBus state machine
Date: Mon, 26 Nov 2018 14:04:24 -0600 [thread overview]
Message-ID: <20181126200435.23408-6-minyard@acm.org> (raw)
In-Reply-To: <20181126200435.23408-1-minyard@acm.org>
From: Corey Minyard <cminyard@mvista.com>
The SMBus slave code had an unneeded state, unnecessary function
pointers and incorrectly handled quick commands. Rewrite it
to simplify the code and make it work correctly.
smbus_eeprom is the only user, so no other effects and the eeprom
code also gets a significant simplification.
Signed-off-by: Corey Minyard <cminyard@mvista.com>
---
hw/i2c/smbus_eeprom.c | 58 ++++++-----------------
hw/i2c/smbus_slave.c | 91 ++++++++++++++++--------------------
include/hw/i2c/smbus_slave.h | 45 +++++++++++++-----
3 files changed, 86 insertions(+), 108 deletions(-)
diff --git a/hw/i2c/smbus_eeprom.c b/hw/i2c/smbus_eeprom.c
index d82423aa7e..4d25222e23 100644
--- a/hw/i2c/smbus_eeprom.c
+++ b/hw/i2c/smbus_eeprom.c
@@ -36,28 +36,12 @@ typedef struct SMBusEEPROMDevice {
uint8_t offset;
} SMBusEEPROMDevice;
-static void eeprom_quick_cmd(SMBusDevice *dev, uint8_t read)
-{
-#ifdef DEBUG
- printf("eeprom_quick_cmd: addr=0x%02x read=%d\n", dev->i2c.address, read);
-#endif
-}
-
-static void eeprom_send_byte(SMBusDevice *dev, uint8_t val)
-{
- SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *) dev;
-#ifdef DEBUG
- printf("eeprom_send_byte: addr=0x%02x val=0x%02x\n",
- dev->i2c.address, val);
-#endif
- eeprom->offset = val;
-}
-
static uint8_t eeprom_receive_byte(SMBusDevice *dev)
{
SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *) dev;
uint8_t *data = eeprom->data;
uint8_t val = data[eeprom->offset++];
+
#ifdef DEBUG
printf("eeprom_receive_byte: addr=0x%02x val=0x%02x\n",
dev->i2c.address, val);
@@ -65,37 +49,26 @@ static uint8_t eeprom_receive_byte(SMBusDevice *dev)
return val;
}
-static void eeprom_write_data(SMBusDevice *dev, uint8_t cmd, uint8_t *buf, int len)
+static int eeprom_write_data(SMBusDevice *dev, uint8_t *buf, uint8_t len)
{
SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *) dev;
- int n;
+ uint8_t *data = eeprom->data;
+
#ifdef DEBUG
printf("eeprom_write_byte: addr=0x%02x cmd=0x%02x val=0x%02x\n",
dev->i2c.address, cmd, buf[0]);
#endif
- /* A page write operation is not a valid SMBus command.
- It is a block write without a length byte. Fortunately we
- get the full block anyway. */
- /* TODO: Should this set the current location? */
- if (cmd + len > 256)
- n = 256 - cmd;
- else
- n = len;
- memcpy(eeprom->data + cmd, buf, n);
- len -= n;
- if (len)
- memcpy(eeprom->data, buf + n, len);
-}
+ /* len is guaranteed to be > 0 */
+ eeprom->offset = buf[0];
+ buf++;
+ len--;
+
+ for (; len > 0; len--) {
+ data[eeprom->offset] = *buf++;
+ eeprom->offset = (eeprom->offset + 1) % 256;
+ }
-static uint8_t eeprom_read_data(SMBusDevice *dev, uint8_t cmd, int n)
-{
- SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *) dev;
- /* If this is the first byte then set the current position. */
- if (n == 0)
- eeprom->offset = cmd;
- /* As with writes, we implement block reads without the
- SMBus length byte. */
- return eeprom_receive_byte(dev);
+ return 0;
}
static void smbus_eeprom_realize(DeviceState *dev, Error **errp)
@@ -116,11 +89,8 @@ static void smbus_eeprom_class_initfn(ObjectClass *klass, void *data)
SMBusDeviceClass *sc = SMBUS_DEVICE_CLASS(klass);
dc->realize = smbus_eeprom_realize;
- sc->quick_cmd = eeprom_quick_cmd;
- sc->send_byte = eeprom_send_byte;
sc->receive_byte = eeprom_receive_byte;
sc->write_data = eeprom_write_data;
- sc->read_data = eeprom_read_data;
dc->props = smbus_eeprom_properties;
/* Reason: pointer property "data" */
dc->user_creatable = false;
diff --git a/hw/i2c/smbus_slave.c b/hw/i2c/smbus_slave.c
index 549e7ae933..70ff29c095 100644
--- a/hw/i2c/smbus_slave.c
+++ b/hw/i2c/smbus_slave.c
@@ -34,7 +34,6 @@ do { fprintf(stderr, "smbus: error: " fmt , ## __VA_ARGS__);} while (0)
enum {
SMBUS_IDLE,
SMBUS_WRITE_DATA,
- SMBUS_RECV_BYTE,
SMBUS_READ_DATA,
SMBUS_DONE,
SMBUS_CONFUSED = -1
@@ -54,20 +53,9 @@ static void smbus_do_write(SMBusDevice *dev)
{
SMBusDeviceClass *sc = SMBUS_DEVICE_GET_CLASS(dev);
- if (dev->data_len == 0) {
- smbus_do_quick_cmd(dev, 0);
- } else if (dev->data_len == 1) {
- DPRINTF("Send Byte\n");
- if (sc->send_byte) {
- sc->send_byte(dev, dev->data_buf[0]);
- }
- } else {
- dev->command = dev->data_buf[0];
- DPRINTF("Command %d len %d\n", dev->command, dev->data_len - 1);
- if (sc->write_data) {
- sc->write_data(dev, dev->command, dev->data_buf + 1,
- dev->data_len - 1);
- }
+ DPRINTF("Command %d len %d\n", dev->data_buf[0], dev->data_len);
+ if (sc->write_data) {
+ sc->write_data(dev, dev->data_buf, dev->data_len);
}
}
@@ -82,6 +70,7 @@ static int smbus_i2c_event(I2CSlave *s, enum i2c_event event)
DPRINTF("Incoming data\n");
dev->mode = SMBUS_WRITE_DATA;
break;
+
default:
BADF("Unexpected send start condition in state %d\n", dev->mode);
dev->mode = SMBUS_CONFUSED;
@@ -93,25 +82,20 @@ static int smbus_i2c_event(I2CSlave *s, enum i2c_event event)
switch (dev->mode) {
case SMBUS_IDLE:
DPRINTF("Read mode\n");
- dev->mode = SMBUS_RECV_BYTE;
+ dev->mode = SMBUS_READ_DATA;
break;
+
case SMBUS_WRITE_DATA:
if (dev->data_len == 0) {
BADF("Read after write with no data\n");
dev->mode = SMBUS_CONFUSED;
} else {
- if (dev->data_len > 1) {
- smbus_do_write(dev);
- } else {
- dev->command = dev->data_buf[0];
- DPRINTF("%02x: Command %d\n", dev->i2c.address,
- dev->command);
- }
+ smbus_do_write(dev);
DPRINTF("Read mode\n");
- dev->data_len = 0;
dev->mode = SMBUS_READ_DATA;
}
break;
+
default:
BADF("Unexpected recv start condition in state %d\n", dev->mode);
dev->mode = SMBUS_CONFUSED;
@@ -120,19 +104,29 @@ static int smbus_i2c_event(I2CSlave *s, enum i2c_event event)
break;
case I2C_FINISH:
- switch (dev->mode) {
- case SMBUS_WRITE_DATA:
- smbus_do_write(dev);
- break;
- case SMBUS_RECV_BYTE:
- smbus_do_quick_cmd(dev, 1);
- break;
- case SMBUS_READ_DATA:
- BADF("Unexpected stop during receive\n");
- break;
- default:
- /* Nothing to do. */
- break;
+ if (dev->data_len == 0) {
+ if (dev->mode == SMBUS_WRITE_DATA || dev->mode == SMBUS_READ_DATA) {
+ smbus_do_quick_cmd(dev, dev->mode == SMBUS_READ_DATA);
+ }
+ } else {
+ SMBusDeviceClass *sc = SMBUS_DEVICE_GET_CLASS(dev);
+
+ switch (dev->mode) {
+ case SMBUS_WRITE_DATA:
+ smbus_do_write(dev);
+ break;
+
+ case SMBUS_READ_DATA:
+ BADF("Unexpected stop during receive\n");
+ break;
+
+ default:
+ /* Nothing to do. */
+ break;
+ }
+ if (sc->transaction_complete) {
+ sc->transaction_complete(dev);
+ }
}
dev->mode = SMBUS_IDLE;
dev->data_len = 0;
@@ -143,9 +137,11 @@ static int smbus_i2c_event(I2CSlave *s, enum i2c_event event)
case SMBUS_DONE:
/* Nothing to do. */
break;
+
case SMBUS_READ_DATA:
dev->mode = SMBUS_DONE;
break;
+
default:
BADF("Unexpected NACK in state %d\n", dev->mode);
dev->mode = SMBUS_CONFUSED;
@@ -160,33 +156,22 @@ static uint8_t smbus_i2c_recv(I2CSlave *s)
{
SMBusDevice *dev = SMBUS_DEVICE(s);
SMBusDeviceClass *sc = SMBUS_DEVICE_GET_CLASS(dev);
- uint8_t ret;
+ uint8_t ret = 0xff;
switch (dev->mode) {
- case SMBUS_RECV_BYTE:
+ case SMBUS_READ_DATA:
if (sc->receive_byte) {
ret = sc->receive_byte(dev);
- } else {
- ret = 0;
- }
- DPRINTF("Receive Byte %02x\n", ret);
- dev->mode = SMBUS_DONE;
- break;
- case SMBUS_READ_DATA:
- if (sc->read_data) {
- ret = sc->read_data(dev, dev->command, dev->data_len);
- dev->data_len++;
- } else {
- ret = 0;
}
DPRINTF("Read data %02x\n", ret);
break;
+
default:
BADF("Unexpected read in state %d\n", dev->mode);
dev->mode = SMBUS_CONFUSED;
- ret = 0;
break;
}
+
return ret;
}
@@ -199,10 +184,12 @@ static int smbus_i2c_send(I2CSlave *s, uint8_t data)
DPRINTF("Write data %02x\n", data);
dev->data_buf[dev->data_len++] = data;
break;
+
default:
BADF("Unexpected write in state %d\n", dev->mode);
break;
}
+
return 0;
}
diff --git a/include/hw/i2c/smbus_slave.h b/include/hw/i2c/smbus_slave.h
index ff07ee005d..fad2db9c76 100644
--- a/include/hw/i2c/smbus_slave.h
+++ b/include/hw/i2c/smbus_slave.h
@@ -38,19 +38,41 @@
typedef struct SMBusDeviceClass
{
I2CSlaveClass parent_class;
+
+ /*
+ * An operation with no data, special in SMBus.
+ * This may be NULL, quick commands are ignore in that case.
+ */
void (*quick_cmd)(SMBusDevice *dev, uint8_t read);
- void (*send_byte)(SMBusDevice *dev, uint8_t val);
+
+ /*
+ * We can't distinguish between a word write and a block write with
+ * length 1, so pass the whole data block including the length byte
+ * (if present). The device is responsible figuring out what type of
+ * command this is.
+ * This may be NULL if no data is written to the device. Writes
+ * will be ignore in that case.
+ */
+ int (*write_data)(SMBusDevice *dev, uint8_t *buf, uint8_t len);
+
+ /*
+ * Likewise we can't distinguish between different reads, or even know
+ * the length of the read until the read is complete, so read data a
+ * byte at a time. The device is responsible for adding the length
+ * byte on block reads. This call cannot fail, it should return
+ * something, preferably 0xff if nothing is available.
+ * This may be NULL if no data is read from the device. Reads will
+ * return 0xff in that case.
+ */
uint8_t (*receive_byte)(SMBusDevice *dev);
- /* We can't distinguish between a word write and a block write with
- length 1, so pass the whole data block including the length byte
- (if present). The device is responsible figuring out what type of
- command this is. */
- void (*write_data)(SMBusDevice *dev, uint8_t cmd, uint8_t *buf, int len);
- /* Likewise we can't distinguish between different reads, or even know
- the length of the read until the read is complete, so read data a
- byte at a time. The device is responsible for adding the length
- byte on block reads. */
- uint8_t (*read_data)(SMBusDevice *dev, uint8_t cmd, int n);
+
+ /*
+ * Called whan an SMBus transaction has completed. This can be used
+ * so the device knows when an operation completes. This is not
+ * called after quick commands, those are complete by nature.
+ * This may be NULL if the device doesn't need this.
+ */
+ void (*transaction_complete)(SMBusDevice *dev);
} SMBusDeviceClass;
struct SMBusDevice {
@@ -61,7 +83,6 @@ struct SMBusDevice {
int mode;
int data_len;
uint8_t data_buf[34]; /* command + len + 32 bytes of data. */
- uint8_t command;
};
#endif
--
2.17.1
next prev parent reply other threads:[~2018-11-26 20:05 UTC|newest]
Thread overview: 47+ messages / expand[flat|nested] mbox.gz Atom feed top
2018-11-26 20:04 [Qemu-devel] [PATCH v3 00/16] Fix/add vmstate handling in some I2C code minyard
2018-11-26 20:04 ` [Qemu-devel] [PATCH v3 01/16] i2c: Split smbus into parts minyard
2018-11-26 20:23 ` Philippe Mathieu-Daudé
2018-11-26 20:04 ` [Qemu-devel] [PATCH v3 02/16] i2c: have I2C receive operation return uint8_t minyard
2018-11-26 20:23 ` Philippe Mathieu-Daudé
2018-11-27 0:14 ` Corey Minyard
2018-11-26 20:04 ` [Qemu-devel] [PATCH v3 03/16] arm:i2c: Don't mask return from i2c_recv() minyard
2018-11-26 20:29 ` Philippe Mathieu-Daudé
2018-11-30 17:26 ` Peter Maydell
2018-11-26 20:04 ` [Qemu-devel] [PATCH v3 04/16] i2c: Don't check return value " minyard
2018-11-30 17:25 ` Peter Maydell
2018-11-30 18:53 ` Corey Minyard
2018-11-26 20:04 ` minyard [this message]
2018-11-30 18:13 ` [Qemu-devel] [PATCH v3 05/16] i2c: Simplify and correct the SMBus state machine Peter Maydell
2018-11-30 21:03 ` Corey Minyard
2018-11-26 20:04 ` [Qemu-devel] [PATCH v3 06/16] i2c: Add a length check to the SMBus write handling minyard
2018-11-26 20:33 ` Philippe Mathieu-Daudé
2018-11-26 20:04 ` [Qemu-devel] [PATCH v3 07/16] i2c:pm_smbus: Fix pm_smbus handling of I2C block read minyard
2018-11-26 20:04 ` [Qemu-devel] [PATCH v3 08/16] boards.h: Ignore migration for SMBus devices on older machines minyard
2018-11-29 12:20 ` Dr. David Alan Gilbert
2018-11-26 20:04 ` [Qemu-devel] [PATCH v3 09/16] migration: Add a VMSTATE_BOOL_TEST() macro minyard
2018-11-29 12:22 ` Dr. David Alan Gilbert
2018-11-26 20:04 ` [Qemu-devel] [PATCH v3 10/16] i2c:pm_smbus: Fix state transfer minyard
2018-11-29 12:28 ` Dr. David Alan Gilbert
2018-11-26 20:04 ` [Qemu-devel] [PATCH v3 11/16] i2c:smbus_slave: Add an SMBus vmstate structure minyard
2018-11-29 13:09 ` Dr. David Alan Gilbert
2018-11-26 20:04 ` [Qemu-devel] [PATCH v3 12/16] i2c:smbus_eeprom: Add normal type name and cast to smbus_eeprom.c minyard
2018-11-26 20:35 ` Philippe Mathieu-Daudé
2018-11-26 20:04 ` [Qemu-devel] [PATCH v3 13/16] i2c:smbus_eeprom: Add a size constant for the smbus_eeprom size minyard
2018-11-26 20:04 ` [Qemu-devel] [PATCH v3 14/16] i2c:smbus_eeprom: Add vmstate handling to the smbus eeprom minyard
2018-11-29 13:29 ` Dr. David Alan Gilbert
2018-12-19 18:52 ` Corey Minyard
2019-01-03 10:44 ` Dr. David Alan Gilbert
2018-11-26 20:04 ` [Qemu-devel] [PATCH v3 15/16] hw/i2c/smbus_eeprom: Create at most SMBUS_EEPROM_MAX EEPROMs on a SMBus minyard
2018-11-30 17:39 ` Peter Maydell
2018-11-30 20:47 ` Corey Minyard
2018-12-01 11:57 ` Peter Maydell
2018-12-01 17:43 ` Philippe Mathieu-Daudé
2018-12-03 21:19 ` Corey Minyard
2018-11-26 20:04 ` [Qemu-devel] [PATCH v3 16/16] i2c:smbus_eeprom: Add a reset function to smbus_eeprom minyard
2018-11-26 20:42 ` Philippe Mathieu-Daudé
2018-11-26 22:41 ` Corey Minyard
2018-11-26 23:01 ` Philippe Mathieu-Daudé
2018-11-26 23:58 ` Corey Minyard
2018-11-27 10:54 ` Philippe Mathieu-Daudé
2018-11-27 12:58 ` Corey Minyard
2018-11-27 13:27 ` Philippe Mathieu-Daudé
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20181126200435.23408-6-minyard@acm.org \
--to=minyard@acm.org \
--cc=cminyard@mvista.com \
--cc=dgilbert@redhat.com \
--cc=mst@redhat.com \
--cc=pbonzini@redhat.com \
--cc=peter.maydell@linaro.org \
--cc=philmd@redhat.com \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).