summaryrefslogtreecommitdiff
path: root/src/plugins/serial/serial_basic.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/serial/serial_basic.c')
-rw-r--r--src/plugins/serial/serial_basic.c1029
1 files changed, 1029 insertions, 0 deletions
diff --git a/src/plugins/serial/serial_basic.c b/src/plugins/serial/serial_basic.c
new file mode 100644
index 0000000..5f4b926
--- /dev/null
+++ b/src/plugins/serial/serial_basic.c
@@ -0,0 +1,1029 @@
+/*
+ * Copyright (c) 2012 Pigeon Point Systems. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Neither the name of Pigeon Point Systems nor the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any kind.
+ * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
+ * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
+ * PIGEON POINT SYSTEMS ("PPS") AND ITS LICENSORS SHALL NOT BE LIABLE
+ * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
+ * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL
+ * PPS OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA,
+ * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR
+ * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF
+ * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
+ * EVEN IF PPS HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ */
+
+/* Serial Interface, Basic Mode plugin. */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/poll.h>
+#include <termios.h>
+
+#include <ipmitool/ipmi.h>
+#include <ipmitool/ipmi_intf.h>
+#include <ipmitool/helper.h>
+#include <ipmitool/log.h>
+
+#if defined(HAVE_CONFIG_H)
+# include <config.h>
+#endif
+
+#define SERIAL_BM_MAX_MSG_SIZE 47
+#define SERIAL_BM_MAX_RQ_SIZE 33 /* 40 - 7 */
+#define SERIAL_BM_MAX_RS_SIZE 32 /* 40 - 8 */
+#define SERIAL_BM_TIMEOUT 5
+#define SERIAL_BM_RETRY_COUNT 5
+#define SERIAL_BM_MAX_BUFFER_SIZE 250
+
+#define BM_START 0xA0
+#define BM_STOP 0xA5
+#define BM_HANDSHAKE 0xA6
+#define BM_ESCAPE 0xAA
+
+/*
+ * IPMB message header
+ */
+struct ipmb_msg_hdr {
+ unsigned char rsSA;
+ unsigned char netFn; /* NET FN | RS LUN */
+ unsigned char csum1;
+ unsigned char rqSA;
+ unsigned char rqSeq; /* RQ SEQ | RQ LUN */
+ unsigned char cmd;
+ unsigned char data[0];
+};
+
+/*
+ * Send Message command request for IPMB-format
+ */
+struct ipmi_send_message_rq {
+ unsigned char channel;
+ struct ipmb_msg_hdr msg;
+};
+
+/*
+ * Get Message command response for IPMB-format
+ */
+struct ipmi_get_message_rp {
+ unsigned char completion;
+ unsigned char channel;
+ unsigned char netFn;
+ unsigned char csum1;
+ unsigned char rsSA;
+ unsigned char rqSeq;
+ unsigned char cmd;
+ unsigned char data[0];
+};
+
+/*
+ * State for the received message
+ */
+enum {
+ MSG_NONE,
+ MSG_IN_PROGRESS,
+ MSG_DONE
+};
+
+/*
+ * Message parsing context
+ */
+struct serial_bm_parse_ctx{
+ int state;
+ uint8_t * msg;
+ size_t msg_len;
+ size_t max_len;
+ int escape;
+};
+
+/*
+ * Receiving context
+ */
+struct serial_bm_recv_ctx {
+ char buffer[SERIAL_BM_MAX_BUFFER_SIZE];
+ size_t buffer_size;
+ size_t max_buffer_size;
+};
+
+/*
+ * Sending context
+ */
+struct serial_bm_request_ctx {
+ uint8_t rsSA;
+ uint8_t netFn;
+ uint8_t rqSA;
+ uint8_t rqSeq;
+ uint8_t cmd;
+};
+
+/*
+ * Table of supported baud rates
+ */
+static const struct {
+ int baudinit;
+ int baudrate;
+} rates[] = {
+ { B2400, 2400 },
+ { B9600, 9600 },
+ { B19200, 19200 },
+ { B38400, 38400 },
+ { B57600, 57600 },
+ { B115200, 115200 },
+ { B230400, 230400 },
+#ifdef B460800
+ { B460800, 460800 },
+#endif
+};
+
+/*
+ * Table of special characters
+ */
+static const struct {
+ uint8_t character;
+ uint8_t escape;
+} characters[] = {
+ { BM_START, 0xB0 }, /* start */
+ { BM_STOP, 0xB5 }, /* stop */
+ { BM_HANDSHAKE, 0xB6 }, /* packet handshake */
+ { BM_ESCAPE, 0xBA }, /* data escape */
+ { 0x1B, 0x3B } /* escape */
+};
+
+static int is_system;
+
+/*
+ * Setup serial interface
+ */
+static int
+serial_bm_setup(struct ipmi_intf * intf)
+{
+ intf->session = malloc(sizeof(struct ipmi_session));
+ if (intf->session == NULL) {
+ lprintf(LOG_ERR, "ipmitool: malloc failure");
+ return -1;
+ }
+ memset(intf->session, 0, sizeof(struct ipmi_session));
+
+ /* setup default LAN maximum request and response sizes */
+ intf->max_request_data_size = SERIAL_BM_MAX_RQ_SIZE;
+ intf->max_response_data_size = SERIAL_BM_MAX_RS_SIZE;
+ return 0;
+}
+
+/*
+ * Open serial interface
+ */
+static int
+serial_bm_open(struct ipmi_intf * intf)
+{
+ struct termios ti;
+ unsigned int rate = 9600;
+ char *p;
+ int i;
+
+ if (!intf->devfile) {
+ lprintf(LOG_ERR, "Serial device is not specified");
+ return -1;
+ }
+
+ is_system = 0;
+
+ /* check if baud rate is specified */
+ if ((p = strchr(intf->devfile, ':'))) {
+ char * pp;
+
+ /* separate device name from baud rate */
+ *p++ = '\0';
+
+ /* check for second colon */
+ if ((pp = strchr(p, ':'))) {
+ /* this is needed to normally acquire baud rate */
+ *pp++ = '\0';
+
+ /* check if it is a system interface */
+ if (pp[0] == 'S' || pp[0] == 's') {
+ is_system = 1;
+ }
+ }
+
+ if (str2uint(p, &rate)) {
+ lprintf(LOG_ERR, "Invalid baud rate specified\n");
+ return -1;
+ }
+ }
+
+ intf->fd = open(intf->devfile, O_RDWR | O_NONBLOCK, 0);
+ if (intf->fd < 0) {
+ lperror(LOG_ERR, "Could not open device at %s", intf->devfile);
+ return -1;
+ }
+
+ for (i = 0; i < sizeof(rates) / sizeof(rates[0]); i++) {
+ if (rates[i].baudrate == rate) {
+ break;
+ }
+ }
+ if (i >= sizeof(rates) / sizeof(rates[0])) {
+ lprintf(LOG_ERR, "Unsupported baud rate %i specified", rate);
+ return -1;
+ }
+
+ tcgetattr(intf->fd, &ti);
+
+ cfsetispeed(&ti, rates[i].baudinit);
+ cfsetospeed(&ti, rates[i].baudinit);
+
+ /* 8N1 */
+ ti.c_cflag &= ~PARENB;
+ ti.c_cflag &= ~CSTOPB;
+ ti.c_cflag &= ~CSIZE;
+ ti.c_cflag |= CS8;
+
+ /* enable the receiver and set local mode */
+ ti.c_cflag |= (CLOCAL | CREAD);
+
+ /* no flow control */
+ ti.c_cflag &= ~CRTSCTS;
+ ti.c_iflag &= ~(IGNBRK | IGNCR | INLCR | ICRNL | INPCK | ISTRIP
+ | IXON | IXOFF | IXANY);
+#ifdef IUCLC
+ /* Only disable uppercase-to-lowercase mapping on input for
+ platforms supporting the flag. */
+ ti.c_iflag &= ~(IUCLC);
+#endif
+
+
+ ti.c_oflag &= ~(OPOST);
+ ti.c_lflag &= ~(ICANON | ISIG | ECHO | ECHONL | NOFLSH);
+
+ /* set the new options for the port with flushing */
+ tcsetattr(intf->fd, TCSAFLUSH, &ti);
+
+ if (intf->session->timeout == 0)
+ intf->session->timeout = SERIAL_BM_TIMEOUT;
+ if (intf->session->retry == 0)
+ intf->session->retry = SERIAL_BM_RETRY_COUNT;
+
+ intf->opened = 1;
+
+ return 0;
+}
+
+/*
+ * Close serial interface
+ */
+static void
+serial_bm_close(struct ipmi_intf * intf)
+{
+ if (intf->opened) {
+ close(intf->fd);
+ intf->fd = -1;
+ }
+
+ if (intf->session) {
+ free(intf->session);
+ intf->session = NULL;
+ }
+
+ intf->opened = 0;
+}
+
+/*
+ * Allocate sequence number for tracking
+ */
+static uint8_t
+serial_bm_alloc_seq(void)
+{
+ static uint8_t seq = 0;
+ if (++seq == 64) {
+ seq = 0;
+ }
+ return seq;
+}
+
+/*
+ * Flush the buffers
+ */
+static int
+serial_bm_flush(struct ipmi_intf * intf)
+{
+#if defined(TCFLSH)
+ return ioctl(intf->fd, TCFLSH, TCIOFLUSH);
+#elif defined(TIOCFLUSH)
+ return ioctl(intf->fd, TIOCFLUSH);
+#else
+# error "unsupported platform, missing flush support (TCFLSH/TIOCFLUSH)"
+#endif
+}
+
+/*
+ * Return escaped character for the given one
+ */
+static inline uint8_t
+serial_bm_get_escaped_char(uint8_t c)
+{
+ int i;
+
+ for (i = 0; i < 5; i++) {
+ if (characters[i].character == c) {
+ return characters[i].escape;
+ }
+ }
+
+ return c;
+}
+
+/*
+ * Return unescaped character for the given one
+ */
+static inline uint8_t
+serial_bm_get_unescaped_char(uint8_t c)
+{
+ int i;
+
+ for (i = 0; i < 5; i++) {
+ if (characters[i].escape == c) {
+ return characters[i].character;
+ }
+ }
+
+ return c;
+}
+
+/*
+ * Send message to serial port
+ */
+static int
+serial_bm_send_msg(struct ipmi_intf * intf, uint8_t * msg, int msg_len)
+{
+ int i, size, tmp = 0;
+ uint8_t * buf, * data;
+
+ if (verbose > 3) {
+ fprintf(stderr, "Sending request:\n");
+ fprintf(stderr, " rsSA = 0x%x\n", msg[0]);
+ fprintf(stderr, " NetFN/rsLUN = 0x%x\n", msg[1]);
+ fprintf(stderr, " rqSA = 0x%x\n", msg[3]);
+ fprintf(stderr, " rqSeq/rqLUN = 0x%x\n", msg[4]);
+ fprintf(stderr, " cmd = 0x%x\n", msg[5]);
+ if (msg_len > 7) {
+ fprintf(stderr, " data_len = %d\n", msg_len - 7);
+ fprintf(stderr, " data = %s\n",
+ buf2str(msg + 6, msg_len - 7));
+ }
+ }
+
+ if (verbose > 4) {
+ fprintf(stderr, "Message data:\n");
+ fprintf(stderr, " %s\n", buf2str(msg, msg_len));
+ }
+
+ /* calculate escaped characters number */
+ for (i = 0; i < msg_len; i++) {
+ if (serial_bm_get_escaped_char(msg[i]) != msg[i]) {
+ tmp++;
+ }
+ }
+
+ /* calculate required buffer size */
+ size = msg_len + tmp + 2;
+
+ /* allocate buffer for output data */
+ buf = data = (uint8_t *) alloca(size);
+
+ if (!buf) {
+ lperror(LOG_ERR, "ipmitool: alloca error");
+ return -1;
+ }
+
+ /* start character */
+ *buf++ = 0xA0;
+
+ for (i = 0; i < msg_len; i++) {
+ tmp = serial_bm_get_escaped_char(msg[i]);
+ if (tmp != msg[i]) {
+ *buf++ = 0xAA;
+ }
+
+ *buf++ = tmp;
+ }
+
+ /* stop character */
+ *buf++ = 0xA5;
+
+ if (verbose > 5) {
+ fprintf(stderr, "Sent serial data:\n %s\n", buf2str(data, size));
+ }
+
+ /* write data to serial port */
+ tmp = write(intf->fd, data, size);
+ if (tmp <= 0) {
+ lperror(LOG_ERR, "ipmitool: write error");
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * This function waits for incoming data
+ */
+static int
+serial_bm_wait_for_data(struct ipmi_intf * intf)
+{
+ int n;
+ struct pollfd pfd;
+
+ pfd.fd = intf->fd;
+ pfd.events = POLLIN;
+ pfd.revents = 0;
+
+ n = poll(&pfd, 1, intf->session->timeout*1000);
+ if (n < 0) {
+ lperror(LOG_ERR, "Poll for serial data failed");
+ return -1;
+ } else if (!n) {
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * This function parses incoming data in basic mode format to IPMB message
+ */
+static int
+serial_bm_parse_buffer(const uint8_t * data, int data_len,
+ struct serial_bm_parse_ctx * ctx)
+{
+ int i, tmp;
+
+ for (i = 0; i < data_len; i++) {
+ /* check for start of new message */
+ if (data[i] == BM_START) {
+ ctx->state = MSG_IN_PROGRESS;
+ ctx->escape = 0;
+ ctx->msg_len = 0;
+ /* check if message is not started */
+ } else if (ctx->state != MSG_IN_PROGRESS) {
+ /* skip character */
+ continue;
+ /* continue escape sequence */
+ } else if (ctx->escape) {
+ /* get original character */
+ tmp = serial_bm_get_unescaped_char(data[i]);
+
+ /* check if not special character */
+ if (tmp == data[i]) {
+ lprintf(LOG_ERR, "ipmitool: bad response");
+ /* reset message state */
+ ctx->state = MSG_NONE;
+ continue;
+ }
+
+ /* check message length */
+ if (ctx->msg_len >= ctx->max_len) {
+ lprintf(LOG_ERR, "ipmitool: response is too long");
+ /* reset message state */
+ ctx->state = MSG_NONE;
+ continue;
+ }
+
+ /* add parsed character */
+ ctx->msg[ctx->msg_len++] = tmp;
+
+ /* clear escape flag */
+ ctx->escape = 0;
+ /* check for escape character */
+ } else if (data[i] == BM_ESCAPE) {
+ ctx->escape = 1;
+ continue;
+ /* check for stop character */
+ } else if (data[i] == BM_STOP) {
+ ctx->state = MSG_DONE;
+ return i + 1;
+ /* check for packet handshake character */
+ } else if (data[i] == BM_HANDSHAKE) {
+ /* just skip it */
+ continue;
+ } else {
+ /* check message length */
+ if (ctx->msg_len >= ctx->max_len) {
+ lprintf(LOG_ERR, "ipmitool: response is too long");
+ return -1;
+ }
+
+ /* add parsed character */
+ ctx->msg[ctx->msg_len++] = data[i];
+ }
+ }
+
+ /* return number of parsed characters */
+ return i;
+}
+
+/*
+ * Read and parse data from serial port
+ */
+static int
+serial_bm_recv_msg(struct ipmi_intf * intf,
+ struct serial_bm_recv_ctx * recv_ctx,
+ uint8_t * msg_data, size_t msg_len)
+{
+ struct serial_bm_parse_ctx parse_ctx;
+ int rv;
+
+ parse_ctx.state = MSG_NONE;
+ parse_ctx.msg = msg_data;
+ parse_ctx.max_len = msg_len;
+
+ do {
+ /* wait for data in the port */
+ if (serial_bm_wait_for_data(intf)) {
+ return 0;
+ }
+
+ /* read data into buffer */
+ rv = read(intf->fd, recv_ctx->buffer + recv_ctx->buffer_size,
+ recv_ctx->max_buffer_size - recv_ctx->buffer_size);
+
+ if (rv < 0) {
+ lperror(LOG_ERR, "ipmitool: read error");
+ return -1;
+ }
+
+ if (verbose > 5) {
+ fprintf(stderr, "Received serial data:\n %s\n",
+ buf2str(recv_ctx->buffer + recv_ctx->buffer_size, rv));
+ }
+
+ /* increment buffer size */
+ recv_ctx->buffer_size += rv;
+
+ /* parse buffer */
+ rv = serial_bm_parse_buffer(recv_ctx->buffer,
+ recv_ctx->buffer_size, &parse_ctx);
+
+ if (rv < recv_ctx->buffer_size) {
+ /* move non-parsed part of the buffer to the beginning */
+ memmove(recv_ctx->buffer, recv_ctx->buffer + rv,
+ recv_ctx->buffer_size - rv);
+ }
+
+ /* decrement buffer size */
+ recv_ctx->buffer_size -= rv;
+ } while (parse_ctx.state != MSG_DONE);
+
+ if (verbose > 4) {
+ printf("Received message:\n %s\n",
+ buf2str(msg_data, parse_ctx.msg_len));
+ }
+
+ /* received a message */
+ return parse_ctx.msg_len;
+}
+
+/*
+ * Build IPMB message to be transmitted
+ */
+static int
+serial_bm_build_msg(const struct ipmi_intf * intf,
+ const struct ipmi_rq * req, uint8_t * msg, size_t max_len,
+ struct serial_bm_request_ctx * ctx, int * msg_len
+ )
+{
+ uint8_t * data = msg, seq;
+ struct ipmb_msg_hdr * hdr = (struct ipmb_msg_hdr *) msg;
+ struct ipmi_send_message_rq * inner_rq = NULL, * outer_rq = NULL;
+ int bridging_level;
+
+ /* acquire bridging level */
+ if (intf->target_addr && intf->target_addr != intf->my_addr) {
+ if (intf->transit_addr != 0) {
+ bridging_level = 2;
+ } else {
+ bridging_level = 1;
+ }
+ } else {
+ bridging_level = 0;
+ }
+
+ /* check overall packet length */
+ if(req->msg.data_len + 7 + bridging_level * 8 > max_len) {
+ lprintf(LOG_ERR, "ipmitool: Message data is too long");
+ return -1;
+ }
+
+ /* allocate new sequence number */
+ seq = serial_bm_alloc_seq() << 2;
+
+ if (bridging_level) {
+ /* compose send message request */
+ hdr->netFn = 0x18;
+ hdr->cmd = 0x34;
+
+ /* set pointer to send message request data */
+ outer_rq = (struct ipmi_send_message_rq *) (hdr + 1);
+
+ /* compose the outer send message request */
+ if (bridging_level == 2) {
+ outer_rq->channel = intf->transit_channel | 0x40;
+ outer_rq->msg.rsSA = intf->transit_addr;
+ outer_rq->msg.netFn = 0x18;
+ outer_rq->msg.csum1 = -(outer_rq->msg.rsSA + outer_rq->msg.netFn);
+ outer_rq->msg.rqSA = intf->my_addr;
+ outer_rq->msg.rqSeq = seq;
+ outer_rq->msg.cmd = 0x34;
+
+ /* inner send message request is further */
+ inner_rq = (outer_rq + 1);
+ } else {
+ /* there is only outer send message reuest */
+ inner_rq = outer_rq;
+ }
+
+ /* compose the inner send message request */
+ inner_rq->channel = intf->target_channel | 0x40;
+ inner_rq->msg.rsSA = intf->target_addr;
+ inner_rq->msg.netFn = (req->msg.netfn << 2) | req->msg.lun;
+ inner_rq->msg.csum1 = -(inner_rq->msg.rsSA + inner_rq->msg.netFn);
+ inner_rq->msg.rqSA = intf->my_addr;
+ inner_rq->msg.rqSeq = seq;
+ inner_rq->msg.cmd = req->msg.cmd;
+
+ /* check if interface is the system one */
+ if (is_system) {
+ /* need response to LUN 2 */
+ outer_rq->msg.rqSeq |= 2;
+
+ /* do not track response */
+ outer_rq->channel &= ~0x40;
+
+ /* restore BMC SA if bridging not to primary IPMB channel */
+ if (outer_rq->channel) {
+ outer_rq->msg.rqSA = IPMI_BMC_SLAVE_ADDR;
+ }
+ }
+
+ /* fill-in the second context */
+ ctx[1].rsSA = outer_rq->msg.rsSA;
+ ctx[1].netFn = outer_rq->msg.netFn;
+ ctx[1].rqSA = outer_rq->msg.rqSA;
+ ctx[1].rqSeq = outer_rq->msg.rqSeq;
+ ctx[1].cmd = outer_rq->msg.cmd;
+
+ /* move write pointer */
+ msg = (uint8_t *)(inner_rq + 1);
+ } else {
+ /* compose direct request */
+ hdr->netFn = (req->msg.netfn << 2) | req->msg.lun;
+ hdr->cmd = req->msg.cmd;
+
+ /* move write pointer */
+ msg = (uint8_t *)(hdr + 1);
+ }
+
+ /* fill-in the rest header fields */
+ hdr->rsSA = IPMI_BMC_SLAVE_ADDR;
+ hdr->csum1 = -(hdr->rsSA + hdr->netFn);
+ hdr->rqSA = IPMI_REMOTE_SWID;
+ hdr->rqSeq = seq;
+
+ /* fill-in the first context */
+ ctx[0].rsSA = hdr->rsSA;
+ ctx[0].netFn = hdr->netFn;
+ ctx[0].rqSA = hdr->rqSA;
+ ctx[0].rqSeq = hdr->rqSeq;
+ ctx[0].cmd = hdr->cmd;
+
+ /* write request data */
+ memcpy(msg, req->msg.data, req->msg.data_len);
+
+ /* move write pointer */
+ msg += req->msg.data_len;
+
+ if (bridging_level) {
+ /* write inner message checksum */
+ *msg++ = ipmi_csum(&inner_rq->msg.rqSA, req->msg.data_len + 3);
+
+ /* check for double bridging */
+ if (bridging_level == 2) {
+ /* write outer message checksum */
+ *msg++ = ipmi_csum(&outer_rq->msg.rqSA, 4);
+ }
+
+ /* write overall message checksum */
+ *msg++ = ipmi_csum(&hdr->rqSA, 4);
+ } else {
+ /* write overall message checksum */
+ *msg++ = ipmi_csum(&hdr->rqSA, req->msg.data_len + 3);
+ }
+
+ /* save message length */
+ *msg_len = msg - data;
+
+ /* return bridging level */
+ return bridging_level;
+}
+
+/*
+ * Wait for request response
+ */
+static int
+serial_bm_wait_response(struct ipmi_intf * intf,
+ struct serial_bm_request_ctx * req_ctx, struct serial_bm_recv_ctx * read_ctx,
+ uint8_t * msg, size_t max_len)
+{
+ struct ipmb_msg_hdr * hdr = (struct ipmb_msg_hdr *) msg;
+ int msg_len, netFn, rqSeq;
+
+ /* receive and match message */
+ while ((msg_len = serial_bm_recv_msg(intf, read_ctx, msg, max_len)) > 0) {
+ /* validate message size */
+ if (msg_len < 8) {
+ lprintf(LOG_ERR, "ipmitool: response is too short");
+ continue;
+ }
+
+ /* validate checksum 1 */
+ if (ipmi_csum(msg, 3)) {
+ lprintf(LOG_ERR, "ipmitool: bad checksum 1");
+ continue;
+ }
+
+ /* validate checksum 2 */
+ if (ipmi_csum(msg + 3, msg_len - 3)) {
+ lprintf(LOG_ERR, "ipmitool: bad checksum 2");
+ continue;
+ }
+
+ /* swap requester and responder LUNs */
+ netFn = ((req_ctx->netFn|4) & ~3) | (req_ctx->rqSeq & 3);
+ rqSeq = (req_ctx->rqSeq & ~3) | (req_ctx->netFn & 3);
+
+ /* check for the waited response */
+ if (hdr->rsSA == req_ctx->rqSA
+ && hdr->netFn == netFn
+ && hdr->rqSA == req_ctx->rsSA
+ && hdr->rqSeq == rqSeq
+ && hdr->cmd == req_ctx->cmd) {
+ /* check if something new has been parsed */
+ if (verbose > 3) {
+ fprintf(stderr, "Got response:\n");
+ fprintf(stderr, " rsSA = 0x%x\n", msg[0]);
+ fprintf(stderr, " NetFN/rsLUN = 0x%x\n", msg[1]);
+ fprintf(stderr, " rqSA = 0x%x\n", msg[3]);
+ fprintf(stderr, " rqSeq/rqLUN = 0x%x\n", msg[4]);
+ fprintf(stderr, " cmd = 0x%x\n", msg[5]);
+ fprintf(stderr, " completion code = 0x%x\n", msg[6]);
+ if (msg_len > 8) {
+ fprintf(stderr, " data_len = %d\n", msg_len - 8);
+ fprintf(stderr, " data = %s\n",
+ buf2str(msg + 7, msg_len - 8));
+ }
+ }
+
+ /* copy only completion and response data */
+ memmove(msg, hdr + 1, msg_len - sizeof (*hdr) - 1);
+
+ /* update message length */
+ msg_len -= sizeof (*hdr) + 1;
+
+ /* the waited one */
+ break;
+ }
+ }
+
+ return msg_len;
+}
+
+/*
+ * Get message from receive message queue
+ */
+static int
+serial_bm_get_message(struct ipmi_intf * intf,
+ struct serial_bm_request_ctx * req_ctx,
+ struct serial_bm_recv_ctx * read_ctx,
+ uint8_t * msg, size_t max_len)
+{
+ uint8_t data[SERIAL_BM_MAX_MSG_SIZE];
+ struct serial_bm_request_ctx tmp_ctx;
+ struct ipmi_get_message_rp * rp = (struct ipmi_get_message_rp *) data;
+ struct ipmb_msg_hdr * hdr = (struct ipmb_msg_hdr *) data;
+ clock_t start, tm;
+ int rv, netFn, rqSeq;
+
+ start = clock();
+
+ do {
+ /* fill-in request context */
+ tmp_ctx.rsSA = IPMI_BMC_SLAVE_ADDR;
+ tmp_ctx.netFn = 0x18;
+ tmp_ctx.rqSA = IPMI_REMOTE_SWID;
+ tmp_ctx.rqSeq = serial_bm_alloc_seq() << 2;
+ tmp_ctx.cmd = 0x33;
+
+ /* fill-in request data */
+ hdr->rsSA = tmp_ctx.rsSA;
+ hdr->netFn = tmp_ctx.netFn;
+ hdr->csum1 = ipmi_csum(data, 2);
+ hdr->rqSA = tmp_ctx.rqSA;
+ hdr->rqSeq = tmp_ctx.rqSeq;
+ hdr->cmd = tmp_ctx.cmd;
+ hdr->data[0] = ipmi_csum(&hdr->rqSA, 3);
+
+ /* send request */
+ serial_bm_flush(intf);
+ serial_bm_send_msg(intf, data, 7);
+
+ /* wait for response */
+ rv = serial_bm_wait_response(intf, &tmp_ctx, read_ctx,
+ data, sizeof (data));
+
+ /* check for IO error or timeout */
+ if (rv <= 0) {
+ return rv;
+ }
+
+ /* check completion code */
+ if (rp->completion == 0) {
+ /* swap requester and responder LUNs */
+ netFn = ((req_ctx->netFn|4) & ~3) | (req_ctx->rqSeq & 3);
+ rqSeq = (req_ctx->rqSeq & ~3) | (req_ctx->netFn & 3);
+
+ /* check for the waited response */
+ if (rp->netFn == netFn
+ && rp->rsSA == req_ctx->rsSA
+ && rp->rqSeq == rqSeq
+ && rp->cmd == req_ctx->cmd) {
+ /* copy the rest of message */
+ memcpy(msg, rp->data, rv - sizeof (*rp) - 1);
+ return rv - sizeof (*rp) - 1;
+ }
+ } else if (rp->completion != 0x80) {
+ return 0;
+ }
+
+ tm = clock() - start;
+
+ tm /= CLOCKS_PER_SEC;
+ } while (tm < intf->session->timeout);
+
+ return 0;
+}
+
+static struct ipmi_rs *
+serial_bm_send_request(struct ipmi_intf * intf, struct ipmi_rq * req)
+{
+ static struct ipmi_rs rsp;
+ uint8_t msg[SERIAL_BM_MAX_MSG_SIZE], * resp = msg;
+ struct serial_bm_request_ctx req_ctx[3];
+ struct serial_bm_recv_ctx read_ctx;
+ int retry, rv, msg_len, bridging_level;
+
+ if (!intf->opened && intf->open && intf->open(intf) < 0) {
+ return NULL;
+ }
+
+ /* reset receive context */
+ read_ctx.buffer_size = 0;
+ read_ctx.max_buffer_size = SERIAL_BM_MAX_BUFFER_SIZE;
+
+ /* Send the message and receive the answer */
+ for (retry = 0; retry < intf->session->retry; retry++) {
+ /* build output message */
+ bridging_level = serial_bm_build_msg(intf, req, msg,
+ sizeof (msg), req_ctx, &msg_len);
+ if (msg_len < 0) {
+ return NULL;
+ }
+
+ /* send request */
+ serial_bm_flush(intf);
+ serial_bm_send_msg(intf, msg, msg_len);
+
+ /* wait for response */
+ rv = serial_bm_wait_response(intf, &req_ctx[0],
+ &read_ctx, msg, sizeof (msg));
+
+ /* check for IO error */
+ if (rv < 0) {
+ return NULL;
+ }
+
+ /* check for timeout */
+ if (rv == 0) {
+ continue;
+ }
+
+ /* check for bridging */
+ if (bridging_level && msg[0] == 0) {
+ /* in the case of payload interface we check receive message queue */
+ if (is_system) {
+ /* check message flags */
+ rv = serial_bm_get_message(intf, &req_ctx[1],
+ &read_ctx, msg, sizeof (msg));
+
+ /* check for IO error */
+ if (rv < 0) {
+ return NULL;
+ }
+
+ /* check for timeout */
+ if (rv == 0) {
+ continue;
+ }
+ /* check if response for inner request is not encapsulated */
+ } else if (rv == 1) {
+ /* wait for response for inner request */
+ rv = serial_bm_wait_response(intf, &req_ctx[0],
+ &read_ctx, msg, sizeof (msg));
+
+ /* check for IO error */
+ if (rv < 0) {
+ return NULL;
+ }
+
+ /* check for timeout */
+ if (rv == 0) {
+ continue;
+ }
+ } else {
+ /* skip outer level header */
+ resp = msg + 7;
+ /* decrement response size */
+ rv -= 8;
+ }
+
+ /* check response size */
+ if (resp[0] == 0 && bridging_level == 2 && rv < 8) {
+ lprintf(LOG_ERR, "ipmitool: Message response is too short");
+ /* invalid message length */
+ return NULL;
+ }
+ }
+
+ /* check for double bridging */
+ if (bridging_level == 2 && resp[0] == 0) {
+ /* get completion code */
+ rsp.ccode = resp[7];
+ rsp.data_len = rv - 9;
+ memcpy(rsp.data, resp + 8, rsp.data_len);
+ } else {
+ rsp.ccode = resp[0];
+ rsp.data_len = rv - 1;
+ memcpy(rsp.data, resp + 1, rsp.data_len);
+ }
+
+ /* return response */
+ return &rsp;
+ }
+
+ /* no valid response */
+ return NULL;
+}
+
+int
+serial_bm_set_my_addr(struct ipmi_intf * intf, uint8_t addr)
+{
+ intf->my_addr = addr;
+ return 0;
+}
+
+/*
+ * Serial BM interface
+ */
+struct ipmi_intf ipmi_serial_bm_intf = {
+ name: "serial-basic",
+ desc: "Serial Interface, Basic Mode",
+ setup: serial_bm_setup,
+ open: serial_bm_open,
+ close: serial_bm_close,
+ sendrecv: serial_bm_send_request,
+ set_my_addr:serial_bm_set_my_addr
+};