summaryrefslogtreecommitdiff
path: root/meta-openbmc-mods/meta-common/recipes-core/interfaces/libmctp/smbus.c
diff options
context:
space:
mode:
Diffstat (limited to 'meta-openbmc-mods/meta-common/recipes-core/interfaces/libmctp/smbus.c')
-rw-r--r--meta-openbmc-mods/meta-common/recipes-core/interfaces/libmctp/smbus.c280
1 files changed, 190 insertions, 90 deletions
diff --git a/meta-openbmc-mods/meta-common/recipes-core/interfaces/libmctp/smbus.c b/meta-openbmc-mods/meta-common/recipes-core/interfaces/libmctp/smbus.c
index 8679a5185..d7c396444 100644
--- a/meta-openbmc-mods/meta-common/recipes-core/interfaces/libmctp/smbus.c
+++ b/meta-openbmc-mods/meta-common/recipes-core/interfaces/libmctp/smbus.c
@@ -2,6 +2,7 @@
#include <assert.h>
#include <stdbool.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@@ -24,7 +25,6 @@
struct mctp_binding_smbus {
struct mctp_binding binding;
- struct mctp *mctp;
int out_fd;
int in_fd;
@@ -50,6 +50,8 @@ struct mctp_binding_smbus {
#define MCTP_SOURCE_SLAVE_ADDRESS 0x21
#define SMBUS_PEC_BYTE_SIZE 1
+#define SMBUS_COMMAND_CODE_SIZE 1
+#define SMBUS_LENGTH_FIELD_SIZE 1
struct mctp_smbus_header_tx {
uint8_t source_slave_address;
@@ -62,54 +64,133 @@ struct mctp_smbus_header_rx {
uint8_t source_slave_address;
};
+#define POLYCHECK (0x1070U << 3)
+static uint8_t crc8_calculate(uint16_t d)
+{
+ int i;
+
+ for (i = 0; i < 8; i++) {
+ if (d & 0x8000) {
+ d = d ^ POLYCHECK;
+ }
+ d = d << 1;
+ }
+
+ return (uint8_t)(d >> 8);
+}
+
+/* Incremental CRC8 over count bytes in the array pointed to by p */
+static uint8_t pec_calculate(uint8_t crc, uint8_t *p, size_t count)
+{
+ int i;
+
+ for (i = 0; i < count; i++) {
+ crc = crc8_calculate((crc ^ p[i]) << 8);
+ }
+
+ return crc;
+}
+
+static uint8_t calculate_pec_byte(uint8_t *buf, size_t len, uint8_t address,
+ uint16_t flags)
+{
+ uint8_t addr = (address << 1) | (flags & I2C_M_RD ? 1 : 0);
+ uint8_t pec = pec_calculate(0, &addr, 1);
+ pec = pec_calculate(pec, buf, len);
+
+ return pec;
+}
+
+static int mctp_smbus_tx(struct mctp_binding_smbus *smbus, uint8_t len)
+{
+
+#ifdef I2C_M_HOLD
+ /* Hold message */
+ static uint16_t holdtimeout = 1000; // timeout in ms.
+ struct i2c_msg msg[2] =
+#else // !I2C_M_HOLD
+ struct i2c_msg msg[1] =
+#endif // I2C_M_HOLD
+ {{.addr = MCTP_SLAVE_ADDRESS,
+ .flags = 0,
+ .len = len,
+ .buf = (__uint8_t *)smbus->txbuf}
+#ifdef I2C_M_HOLD
+ ,
+ {.addr = 0,
+ .flags = I2C_M_HOLD,
+ .len = sizeof(holdtimeout),
+ .buf = (__uint8_t *)&holdtimeout}
+#endif // I2C_M_HOLD
+ };
+
+#ifdef I2C_M_HOLD
+ struct i2c_rdwr_ioctl_data msgrdwr = {&msg, 2};
+#else // !I2C_M_HOLD
+ struct i2c_rdwr_ioctl_data msgrdwr = {&msg, 1};
+#endif // I2C_M_HOLD
+
+ return ioctl(smbus->out_fd, I2C_RDWR, &msgrdwr);
+}
+
+#ifdef I2C_M_HOLD
+static int mctp_smbus_unhold_bus(struct mctp_binding_smbus *smbus)
+{
+ /* Unhold message */
+ static uint16_t holdtimeout = 0; // unhold
+ struct i2c_msg holdmsg = {0, I2C_M_HOLD, sizeof(holdtimeout),
+ (__uint8_t *)&holdtimeout};
+
+ struct i2c_rdwr_ioctl_data msgrdwr = {&holdmsg, 1};
+
+ return ioctl(smbus->out_fd, I2C_RDWR, &msgrdwr);
+}
+#endif // I2C_M_HOLD
+
static int mctp_binding_smbus_tx(struct mctp_binding *b,
struct mctp_pktbuf *pkt)
{
struct mctp_binding_smbus *smbus = binding_to_smbus(b);
struct mctp_smbus_header_tx *hdr;
- uint8_t *buf;
- size_t len;
- int r;
- struct i2c_msg msg;
- struct i2c_rdwr_ioctl_data data;
+ size_t pkt_length;
- data.msgs = &msg;
- data.nmsgs = 1;
+ uint8_t i2c_message_buf[256];
+ uint8_t *buf_ptr;
+ uint8_t i2c_message_len;
uint16_t timeout = 1000;
- int i;
/* the length field in the header excludes smbus framing
* and escape sequences */
- len = mctp_pktbuf_size(pkt);
+ pkt_length = mctp_pktbuf_size(pkt);
- hdr = (void *)smbus->txbuf;
+ buf_ptr = (void *)smbus->txbuf;
+ *buf_ptr = MCTP_COMMAND_CODE;
+ buf_ptr++;
+ *buf_ptr = pkt_length + sizeof(*hdr);
+ buf_ptr++;
- // hdr->len = len + 1; // +1 for PET byte
+ hdr = (void *)buf_ptr;
hdr->source_slave_address = MCTP_SOURCE_SLAVE_ADDRESS;
+ buf_ptr = (buf_ptr + sizeof(*hdr));
+ memcpy(buf_ptr, &pkt->data[pkt->start], pkt_length);
+ buf_ptr = buf_ptr + pkt_length;
- buf = (void *)(hdr + sizeof(*hdr));
+ uint8_t pec_byte = calculate_pec_byte(
+ smbus->txbuf,
+ SMBUS_COMMAND_CODE_SIZE + SMBUS_LENGTH_FIELD_SIZE + sizeof(*hdr)
+ + pkt_length,
+ MCTP_SLAVE_ADDRESS, 0);
- if (len + sizeof(*hdr) > sizeof(smbus->txbuf))
- return -1;
-
- memcpy(buf, &pkt->data[pkt->start], len);
+ *buf_ptr = pec_byte;
- buf += len;
+ i2c_message_len = SMBUS_COMMAND_CODE_SIZE + SMBUS_LENGTH_FIELD_SIZE
+ + sizeof(*hdr) + pkt_length
+ + SMBUS_PEC_BYTE_SIZE; // command code, length,
+ // header, data, pec byte
- if (ioctl(smbus->out_fd, I2C_SLAVE, MCTP_SLAVE_ADDRESS) < 0) {
- mctp_prerr("Can't set slave");
- return -1;
- }
- if (ioctl(smbus->out_fd, I2C_PEC, 1) < 0) {
- mctp_prerr("Cant set PEC byte");
- return -1;
- }
-
- if (i2c_smbus_write_block_data(smbus->out_fd, MCTP_COMMAND_CODE,
- sizeof(*hdr) + len, (void *)smbus->txbuf)
- < 0) {
- mctp_prerr("Failed to send");
+ if (mctp_smbus_tx(smbus, i2c_message_len)) {
+ mctp_prerr("Can't hold mux");
return -1;
}
@@ -119,64 +200,83 @@ static int mctp_binding_smbus_tx(struct mctp_binding *b,
#ifdef MCTP_FILEIO
int mctp_smbus_read(struct mctp_binding_smbus *smbus)
{
- ssize_t len;
- int r;
+ ssize_t len = 0;
struct mctp_smbus_header_rx *hdr;
+ int ret = 0;
- r = lseek(smbus->in_fd, 0, SEEK_SET);
- if (r < 0) {
- mctp_prerr("Failed to seek");
- return -1;
- }
- len = read(smbus->in_fd, smbus->rxbuf, sizeof(smbus->rxbuf));
- if (len < sizeof(*hdr)) {
- // TODO Don't return an error here, as it seems to happen from
- // time to time, even with a properly written poll loop,
- // although it's not clear why.
- return 0;
- }
+ do {
+ ret = lseek(smbus->in_fd, 0, SEEK_SET);
+ if (ret < 0) {
+ mctp_prerr("Failed to seek");
+ ret = -1;
+ }
- hdr = (void *)smbus->rxbuf;
- if (hdr->destination_slave_address
- != (MCTP_SOURCE_SLAVE_ADDRESS & ~1)) {
- mctp_prerr("Got bad slave address %d",
- hdr->destination_slave_address);
- return 0;
- }
- if (hdr->command_code != MCTP_COMMAND_CODE) {
- mctp_prerr("Got bad command code %d", hdr->command_code);
- // Not a payload intended for us
- return 0;
- }
+ len = read(smbus->in_fd, smbus->rxbuf, sizeof(smbus->rxbuf));
+ if (len < sizeof(*hdr)) {
+ // This condition hits from from time to time, even with
+ // a properly written poll loop, although it's not clear
+ // why. Return an error so that the upper layer can
+ // retry.
+ ret = 0;
+ break;
+ }
- if (hdr->byte_count != (len - sizeof(*hdr))) {
- // Got an incorrectly sized payload
- mctp_prerr("Got smbus payload sized %d, expecting %d",
- hdr->byte_count, len - sizeof(*hdr));
- return 0;
- }
+ hdr = (void *)smbus->rxbuf;
+ if (hdr->destination_slave_address
+ != (MCTP_SOURCE_SLAVE_ADDRESS & ~1)) {
+ mctp_prerr("Got bad slave address %d",
+ hdr->destination_slave_address);
+ ret = 0;
+ break;
+ }
+ if (hdr->command_code != MCTP_COMMAND_CODE) {
+ mctp_prerr("Got bad command code %d",
+ hdr->command_code);
+ // Not a payload intended for us
+ ret = 0;
+ break;
+ }
- if (len < 0) {
- mctp_prerr("can't read from smbus device: %m");
- return -1;
- }
+ if (hdr->byte_count != (len - sizeof(*hdr))) {
+ // Got an incorrectly sized payload
+ mctp_prerr("Got smbus payload sized %d, expecting %d",
+ hdr->byte_count, len - sizeof(*hdr));
+ ret = 0;
+ break;
+ }
- smbus->rx_pkt = mctp_pktbuf_alloc(0);
- assert(smbus->rx_pkt);
+ if (len < 0) {
+ mctp_prerr("can't read from smbus device: %m");
+ ret = -1;
+ break;
+ }
- if (mctp_pktbuf_push(smbus->rx_pkt, &smbus->rxbuf[sizeof(*hdr)],
- len - sizeof(*hdr) - SMBUS_PEC_BYTE_SIZE)
- != 0) {
- mctp_prerr("Can't push tok pktbuf: %m");
- return -1;
- }
+ smbus->rx_pkt = mctp_pktbuf_alloc(0);
+ assert(smbus->rx_pkt);
+
+ if (mctp_pktbuf_push(smbus->rx_pkt, &smbus->rxbuf[sizeof(*hdr)],
+ len - sizeof(*hdr) - SMBUS_PEC_BYTE_SIZE)
+ != 0) {
+ mctp_prerr("Can't push tok pktbuf: %m");
+ ret = -1;
+ break;
+ }
- mctp_bus_rx(&(smbus->binding), smbus->rx_pkt);
+ mctp_bus_rx(&(smbus->binding), smbus->rx_pkt);
- mctp_pktbuf_free(smbus->rx_pkt);
- smbus->rx_pkt = NULL;
+ mctp_pktbuf_free(smbus->rx_pkt);
+ smbus->rx_pkt = NULL;
- return 0;
+ } while (0);
+
+#ifdef I2C_M_HOLD
+ if (mctp_smbus_unhold_bus(smbus)) {
+ mctp_prerr("Can't hold mux");
+ ret = -1;
+ }
+#endif // I2C_M_HOLD
+
+ return ret;
}
int mctp_smbus_get_in_fd(struct mctp_binding_smbus *smbus)
@@ -193,17 +293,13 @@ int mctp_smbus_open_bus(struct mctp_binding_smbus *smbus, int out_bus_num,
int root_bus_num)
{
char filename[60];
- size_t filename_size;
+ size_t filename_size = 0;
char slave_mqueue[20];
- size_t mqueue_size;
-
- int fd;
-
- size_t size;
- int address_7_bit;
+ size_t mqueue_size = 0;
+ int fd = 0;
+ size_t size = sizeof(filename);
+ int address_7_bit = MCTP_SOURCE_SLAVE_ADDRESS >> 1;
- address_7_bit = MCTP_SOURCE_SLAVE_ADDRESS >> 1;
- size = sizeof(filename);
snprintf(filename, size,
"/sys/bus/i2c/devices/i2c-%d/%d-%04x/slave-mqueue",
root_bus_num, root_bus_num,
@@ -263,7 +359,6 @@ int mctp_smbus_open_bus(struct mctp_binding_smbus *smbus, int out_bus_num,
return 0;
}
-
#endif
void mctp_smbus_register_bus(struct mctp_binding_smbus *smbus,
@@ -271,8 +366,8 @@ void mctp_smbus_register_bus(struct mctp_binding_smbus *smbus,
{
assert(smbus->out_fd >= 0);
assert(smbus->in_fd >= 0);
- smbus->mctp = mctp;
smbus->bus_id = mctp_register_bus(mctp, &smbus->binding, eid);
+ mctp_binding_set_tx_enabled(&smbus->binding, true);
}
struct mctp_binding_smbus *mctp_smbus_init(void)
@@ -291,3 +386,8 @@ struct mctp_binding_smbus *mctp_smbus_init(void)
return smbus;
}
+
+void mctp_smbus_free(struct mctp_binding_smbus *smbus)
+{
+ __mctp_free(smbus);
+} \ No newline at end of file