root/usr/src/uts/sun4u/io/rmc_comm_dp.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 *
 * implementation of the transport layer protocol (known as librsc protocol):
 *
 */

#pragma ident   "%Z%%M% %I%     %E% SMI"

/*
 *  Header files
 */

#include <sys/conf.h>
#include <sys/cyclic.h>
#include <sys/membar.h>
#include <sys/modctl.h>
#include <sys/strlog.h>
#include <sys/sunddi.h>
#include <sys/ddi.h>
#include <sys/types.h>
#include <sys/rmc_comm_dp.h>
#include <sys/rmc_comm_dp_boot.h>
#include <sys/rmc_comm_drvintf.h>
#include <sys/rmc_comm.h>

#ifdef DEBUG_ERROR_INJECTION

#define ERRI_RX_SEQ_NUMBER      1
#define ERRI_ACK_MSG            2
#define ERRI_CRC_HEADER         3
#define ERRI_CRC_MSG            4
#define ERRI_SEND_CTL_STACK     5
#define ERRI_SEND_CTL_START     6

#define ERRI_CTL_RX_SEQ_NUMBER  7
#define ERRI_CTL_CRC_HEADER     8

int     erri_test_number = 0;
int     erri_test_intrvl = 0;
int     erri_test_repeat = 0;
int     erri_test_count = 0;

int erri_test_simulate_srec_sec(struct rmc_comm_state *, char *, int);

#endif


/* static functions */

static void dp_link_setup_tohandler(void *);
static void dp_delay_ack_tohandler(void *);
static uint8_t *dp_get_buffer(struct rmc_comm_state *, uint8_t);
static void dp_release_buffer(struct rmc_comm_state *, uint8_t);
static void dp_init_buffers(struct rmc_comm_state *);
static void dp_got_full_hdr(struct rmc_comm_state *, dp_packet_t *);
static void dp_got_bp_msg(struct rmc_comm_state *, dp_packet_t *);
static void dp_got_full_msg(struct rmc_comm_state *, dp_packet_t *);
static void dp_tx_handle_ack(struct rmc_comm_state *, uint16_t);
static void dp_tx_handle_nak(struct rmc_comm_state *, uint16_t);
static void dp_send_packet(struct rmc_comm_state *, uchar_t *);
static void dp_enable_data_link(struct rmc_comm_state *);
static int dp_get_msglen(struct rmc_comm_state *, uint8_t *);
static uint16_t dp_calc_crc16(uint8_t *, int);
void dp_wake_up_waiter(struct rmc_comm_state *, uint8_t);
void dp_reset(struct rmc_comm_state *, uint8_t, boolean_t, boolean_t);

/*
 * utilities...
 */

/*
 *  init rx/tx buffer pool
 */
static void
dp_init_buffers(struct rmc_comm_state *rcs)
{
        int i;
        dp_buffer_t *dbuf = rcs->dp_state.dp_buffers;

        for (i = 0; i < DP_BUFFER_COUNT; i++)
                dbuf[i].in_use = 0;
}

/*
 *  get tx/rx buffer
 */
static uint8_t *
dp_get_buffer(struct rmc_comm_state *rcs, uint8_t type)
{
        dp_buffer_t *dbuf = rcs->dp_state.dp_buffers;

        ASSERT(MUTEX_HELD(rcs->dp_state.dp_mutex));

        if ((type != DP_TX_BUFFER && type != DP_RX_BUFFER) ||
                dbuf[type].in_use) {

                DPRINTF(rcs, DMEM,
                        (CE_CONT, "get buffer err. type=%d, in_use=%d\n",
                        type, dbuf[type].in_use));

                return (NULL);
        }

        DPRINTF(rcs, DMEM, (CE_CONT, "get buffer type=%d\n", type));

        dbuf[type].in_use = 1;

        return (dbuf[type].buf);
}

/*
 * release tx/rx buffer
 */
static void
dp_release_buffer(struct rmc_comm_state *rcs, uint8_t type)
{
        dp_buffer_t *dbuf = rcs->dp_state.dp_buffers;

        ASSERT(MUTEX_HELD(rcs->dp_state.dp_mutex));

        if (type != DP_TX_BUFFER && type != DP_RX_BUFFER) {
                DPRINTF(rcs, DMEM,
                        (CE_CONT, "free buffer err. type=%d, in_use=%d\n",
                        type, dbuf[type].in_use));
                return;
        }
        DPRINTF(rcs, DMEM, (CE_CONT, "free buffer type=%d\n", type));

        dbuf[type].in_use = 0;
}

/*
 * setup data link timeout handler
 * (called without having the dp_mutex)
 */
static void
dp_link_setup_tohandler(void *arg)
{
        struct rmc_comm_state *rcs = (struct rmc_comm_state *)arg;
        rmc_comm_dp_state_t *dps = &rcs->dp_state;

        DPRINTF(rcs, DPRO, (CE_CONT, "t/o setup data link\n"));

        /*
         * check if timer has actually been cancelled
         */
        mutex_enter(dps->dp_mutex);
        if (dps->timer_link_setup != (timeout_id_t)0) {

                /*
                 * send CTL:start to the remote side to set up the data link
                 */
                (void) rmc_comm_dp_ctlsend(rcs, DP_CTL_START);

                dps->timer_link_setup = timeout(dp_link_setup_tohandler,
                    (void *) rcs, drv_usectohz(RETRY_DP_SETUP * 1000));
        }
        mutex_exit(dps->dp_mutex);
}

/*
 * delay acknowledgment of a received message timeout handler
 * (called without having the dp_mutex)
 */
static void
dp_delay_ack_tohandler(void *arg)
{
        struct rmc_comm_state *rcs = (struct rmc_comm_state *)arg;
        rmc_comm_dp_state_t *dps = &rcs->dp_state;

#ifdef DEBUG_ERROR_INJECTION

        if (erri_test_number == ERRI_ACK_MSG &&
            erri_test_repeat >= 0 &&
            erri_test_count++ > 0 && !(erri_test_count % erri_test_intrvl)) {

                /*
                 * DON'T ACK THE MESSAGE - BE SILENT!
                 */

                if (erri_test_repeat == 0)
                        erri_test_repeat--; /* will not repeat the test */

                dps->timer_delay_ack = (timeout_id_t)0;
                return;
        }

#endif

        /*
         * check if timer has actually been cancelled
         */
        mutex_enter(dps->dp_mutex);
        if (dps->timer_delay_ack != (timeout_id_t)0) {
                /*
                 * ACK the message
                 */
                (void) rmc_comm_dp_ctlsend(rcs, DP_CTL_ACK);
                dps->timer_delay_ack = (timeout_id_t)0;
        }
        mutex_exit(dps->dp_mutex);
}

/*
 * Enable data link protocol:
 *  stop data link setup timer
 *  set data_link_ok flag
 * (must already have the dp_mutex)
 */
static void
dp_enable_data_link(struct rmc_comm_state *rcs)
{
        rmc_comm_dp_state_t     *dps = &rcs->dp_state;
        timeout_id_t             timer_id;

        ASSERT(MUTEX_HELD(dps->dp_mutex));

        dps->data_link_ok = 1;

        timer_id = dps->timer_link_setup;
        dps->timer_link_setup = (timeout_id_t)0;
        if (timer_id != (timeout_id_t)0) {

                mutex_exit(dps->dp_mutex);
                (void) untimeout(timer_id);
                mutex_enter(dps->dp_mutex);
        }
}

/*
 * CRC calculation routine.
 */
static uint16_t
dp_calc_crc16(uint8_t *buf, int len)
{
        extern uint16_t crctab16[];
        uint16_t crc;

        crc = 0;
        while (len--) {
                crc = (crc >> 8) ^ crctab16[(crc ^ *buf++) & 0xFF];
        }
        return (crc);
}

/*
 * Reset the data protocol
 * (dp_mutex must be held)
 */
void
dp_reset(struct rmc_comm_state *rcs, uint8_t rx_seqid,
    boolean_t flush_tx, boolean_t restart_data_link)
{
        rmc_comm_dp_state_t *dps = &rcs->dp_state;

        ASSERT(MUTEX_HELD(dps->dp_mutex));

        DPRINTF(rcs, DPRO, (CE_CONT,
            "reset proto: rxsid=%d, flushtx=%d, restartdp=%d\n",
            rx_seqid, flush_tx, restart_data_link));

        DPRINTF(rcs, DGEN, (CE_CONT,
            "stats: reset=%d nak=%d start=%d stack=%d retries=%d crcerr=%d\n",
            dps->reset_cnt, dps->nak_cnt, dps->start_cnt, dps->stack_cnt,
            dps->retries_cnt, dps->crcerr_cnt));

        dps->last_rx_seqid = rx_seqid;
        dps->reset_cnt++;

        /*
         * Flush pending tx message.
         */
        if (flush_tx) {
                dps->last_tx_seqid = INITIAL_SEQID;
                dps->last_rx_ack = rx_seqid;

                /*
                 * if there is any pending request/response session
                 * then just abort it.
                 */
                dp_wake_up_waiter(rcs, MSG_ERROR);
        }

        /*
         * restart data link, but only if the data link set up timer is
         * not already running.
         */
        if (restart_data_link && dps->timer_link_setup == (timeout_id_t)0) {

                dps->data_link_ok = 0;

                /*
                 * set up the data protocol link
                 */
                (void) rmc_comm_dp_ctlsend(rcs, DP_CTL_START);
                dps->timer_link_setup = timeout(dp_link_setup_tohandler,
                    (void *)rcs, drv_usectohz(RETRY_DP_SETUP * 1000));
        }
}

/*
 * Handles acknowledgment of a message previously sent OR a heartbeat command
 * (CTL_RESPOND).
 */
static void
dp_tx_handle_ack(struct rmc_comm_state *rcs, uint16_t rxnum)
{
        rmc_comm_dp_state_t     *dps = &rcs->dp_state;
        dp_req_resp_t           *drr = &dps->req_resp;

        ASSERT(MUTEX_HELD(dps->dp_mutex));

        DPRINTF(rcs, DPRO, (CE_CONT, "handle ACK, rxnum=%03d\n", rxnum));

        dps->last_rx_ack = rxnum;
        if ((drr->flags & MSG_SENT) == 0) {
                /*
                 * no pending messages, so nothing to do
                 */
                return;
        }

        if (rxnum == dps->last_tx_seqid) {
                /*
                 * message was sent and acknowledged successfully
                 * set flag and signal the waiting task if it is not
                 * expecting a reply back
                 */
                drr->flags |= MSG_ACKED;
                if (drr->response.msg_type == DP_NULL_MSG) {
                        dp_wake_up_waiter(rcs, MSG_ACKED);
                }
        }
}

/*
 * Handles NAK
 */
static void
dp_tx_handle_nak(struct rmc_comm_state *rcs, uint16_t rxnum)
{
        rmc_comm_dp_state_t     *dps = &rcs->dp_state;
        dp_req_resp_t           *drr = &dps->req_resp;

        ASSERT(MUTEX_HELD(dps->dp_mutex));

        DPRINTF(rcs, DPRO, (CE_CONT, "handle NAK, rxnum=%03d\n", rxnum));

        if ((drr->flags & MSG_SENT) == 0) {
                /*
                 * no pending messages, so nothing to do
                 */
                return;
        }

        /*
         * since one message per time can be sent, it is assumed that the
         * message being NAKed is just the one that has been sent.
         */
        dps->nak_cnt++;

        dp_wake_up_waiter(rcs, MSG_NAKED);
}

/*
 * Got a full header. Check header CRC and get the length of the packet
 */
static void
dp_got_full_hdr(struct rmc_comm_state *rcs, dp_packet_t *pkt)
{
        /*
         * Got the full header.  Call up to the logical layer to see
         * how big of a buffer I need for this message.  If the size
         * is < sizeof (dp_msg_t), then there is something wrong with
         * this message - drop it.  If the size is equal, then hand it
         * up right now. If the size is too big - drop it. otherwise we must
         * receive the body of the message.
         */

        pkt->full_length = dp_get_msglen(rcs, pkt->buf);

        DPRINTF(rcs, DPKT, (CE_CONT, "got header msglen=%d\n",
            pkt->full_length));

        if ((pkt->full_length < 0) ||
            (pkt->full_length < sizeof (dp_header_t)) ||
            (pkt->full_length > DP_BUFFER_SIZE)) {
                /*
                 * not a valid message: either message too big or too small
                 */
                dp_release_buffer(rcs, DP_RX_BUFFER);
                pkt->buf = NULL;

                pkt->rx_state = WAITING_FOR_SYNC;

        } else if (pkt->full_length == sizeof (dp_header_t)) {
                /*
                 * process message: it is basically a control message
                 * (no data being carried)
                 */
                rmc_comm_dp_mrecv(rcs, pkt->buf);

                dp_release_buffer(rcs, DP_RX_BUFFER);
                pkt->buf = NULL;

                pkt->rx_state = WAITING_FOR_SYNC;
        } else {
                pkt->rx_state = RECEIVING_BODY;
        }
}

/*
 * Got a BP (boot prom) message. Usually, BP messages are received when
 * the firmware goes into boot monitor mode (where only BP protocol is used).
 * This just happens during firmware download. There should not be any other
 * case where a BP message is received.
 */
static void
dp_got_bp_msg(struct rmc_comm_state *rcs, dp_packet_t *pkt)
{
        bp_msg_t                *msgp = (bp_msg_t *)pkt->buf;
        rmc_comm_dp_state_t     *dps = &rcs->dp_state;
        dp_req_resp_t           *drr = &dps->req_resp;
        int                     datalen = sizeof (bp_msg_t);

        ASSERT(MUTEX_HELD(dps->dp_mutex));

        /*
         * ignore BP message, if it is not expected
         */
        if ((drr->flags & MSG_SENT_BP) != 0) {

                DPRINTF(rcs, DPRO, (CE_CONT, "got bp msg: %02x %02x %02x\n",
                    msgp->cmd, msgp->dat1, msgp->dat2));

                /*
                 * A boot prom (BP) msg has been sent. Here is the
                 * 'expected' reply
                 */

                /*
                 * check that the recv buffer is big enough (just in case).
                 */
                if (datalen <= drr->response.msg_bufsiz) {
                        bcopy(pkt->buf, drr->response.msg_buf, datalen);
                        drr->response.msg_msglen = datalen;
                        dp_wake_up_waiter(rcs, MSG_RXED_BP);
                } else {
                        drr->response.msg_msglen = -1;
                        dp_wake_up_waiter(rcs, MSG_RXED_BP);
                }
        }

        /* Return the buffer to the pool and wait for the next msg. */
        dp_release_buffer(rcs, DP_RX_BUFFER);
        pkt->buf = NULL;
        pkt->rx_state = WAITING_FOR_SYNC;
}

/*
 * Got a complete message, check CRC and pass it on to the upper layer (message
 * processing)
 */
static void
dp_got_full_msg(struct rmc_comm_state *rcs, dp_packet_t *pkt)
{
        uint16_t         crc;
        int              msglen;

        DPRINTF(rcs, DPKT, (CE_CONT, "got full msg\n"));

        /*
         * check message CRC
         */

        msglen = pkt->full_length - sizeof (dp_header_t) - sizeof (crc);

        bcopy(pkt->buf + (pkt->full_length - sizeof (crc)), &crc, sizeof (crc));

        if (crc == dp_calc_crc16(pkt->buf + sizeof (dp_header_t), msglen)) {
                /*
                 * CRC is ok, process this message
                 */
                DPRINTF(rcs, DPKT, (CE_CONT, "got 'good' msg\n"));

                rmc_comm_dp_mrecv(rcs, pkt->buf);
        } else {
                DPRINTF(rcs, DPKT, (CE_CONT, "CRC error (msg)\n"));
                rcs->dp_state.crcerr_cnt++;
        }

        dp_release_buffer(rcs, DP_RX_BUFFER);
        pkt->buf = NULL;

        pkt->rx_state = WAITING_FOR_SYNC;
}

/*
 * Check the checksum of the header & return the length field.  If the
 * checksum check fails, then return -1.
 */
static int
dp_get_msglen(struct rmc_comm_state *rcs, uint8_t *buf)
{
        dp_header_t     *dp_msgp;
        uint16_t         crc;

        dp_msgp = (dp_header_t *)buf;

        crc = dp_calc_crc16(buf + sizeof (dp_msgp->pad), sizeof (dp_header_t) -
            sizeof (dp_msgp->crc) - sizeof (dp_msgp->pad));

        if (dp_msgp->crc == crc) {
                return (dp_msgp->length + sizeof (dp_msgp->pad));
        } else {
                DPRINTF(rcs, DPKT, (CE_CONT, "CRC error (header)\n"));
                rcs->dp_state.crcerr_cnt++;
                return (-1);
        }
}

/*
 * to send a protocol packet to the remote side. it handles escaping SYNC
 * and ESC chars
 */
static void
dp_send_packet(struct rmc_comm_state *rcs, uchar_t *buf)
{
        char syncbuf[2];
        dp_header_t *dp_msgp = (dp_header_t *)buf;
        int total, cur;

        /* First, send out two SYNC characters. */
        syncbuf[0] = syncbuf[1] = (char)SYNC_CHAR;
        rmc_comm_serdev_send(rcs, syncbuf, 2);

        total = dp_msgp->length;
        buf = buf + sizeof (dp_msgp->pad);

        while (total > 0) {
                cur = 0;

                /* Count up characters that don't need ESC'ing. */
                while ((cur < total) &&
                    (buf[cur] != ESC_CHAR) &&
                    (buf[cur] != SYNC_CHAR)) {
                        cur++;
                }

                /* Send characters that don't need escaping, if any. */
                if (cur > 0) {
                        rmc_comm_serdev_send(rcs, (char *)buf, cur);
                        total -= cur;
                        buf += cur;
                }

                /*
                 * If total > 0 at this point, we need to send an
                 * ESC'd character.  Send as many as there are.
                 */
                while ((total > 0) &&
                    ((*buf == SYNC_CHAR) || (*buf == ESC_CHAR))) {
                        syncbuf[0] = (char)ESC_CHAR;
                        syncbuf[1] = *buf;
                        rmc_comm_serdev_send(rcs, syncbuf, 2);
                        buf++;
                        total--;
                }
        }
}

/*
 * to wake a thread waiting for a reply/ACK/error status for a request/response
 * session.
 */
void
dp_wake_up_waiter(struct rmc_comm_state *rcs, uint8_t flags)
{
        dp_req_resp_t *drr = &rcs->dp_state.req_resp;

        ASSERT(MUTEX_HELD(rcs->dp_state.dp_mutex));

        DPRINTF(rcs, DGEN, (CE_CONT, "wake up? %x, set %x\n",
            (drr->flags & (MSG_SENT | MSG_SENT_BP)) != 0, flags));

        if ((drr->flags & (MSG_SENT | MSG_SENT_BP)) != 0) {
                drr->flags |= flags;
                cv_signal(drr->cv_wait_reply);
        }
}

/*
 * initialization of the data protocol (called from the attach routine)
 */
void
rmc_comm_dp_init(struct rmc_comm_state *rcs)
{
        rmc_comm_dp_state_t *dps = &rcs->dp_state;
        dp_packet_t *pkt = &dps->dp_packet;

        DPRINTF(rcs, DGEN, (CE_CONT, "rmc_comm_dp_init\n"));

        /*
         * initialize data structure:
         */
        bzero((void *) dps, sizeof (rmc_comm_dp_state_t));

        /*
         * initialize packet receive handler state
         */

        pkt->rx_state = WAITING_FOR_SYNC;

        /*
         * cv variables initialization
         * (dp_mutex has been already created during the serial device
         * initialization)
         */
        cv_init(dps->cv_ok_to_send, NULL, CV_DRIVER, NULL);
        cv_init(dps->req_resp.cv_wait_reply, NULL, CV_DRIVER, NULL);

        mutex_enter(dps->dp_mutex);

        dp_init_buffers(rcs);

        /*
         * initialize the data protocol (reset sequence numbers, etc.)
         */
        dps->last_tx_seqid = INITIAL_SEQID;
        dps->last_rx_seqid = dps->last_rx_ack = INITIAL_SEQID;

        /*
         * start timer to 'delay' the set up of the data protocol link
         */
        dps->timer_link_setup = timeout(dp_link_setup_tohandler,
            (void *)rcs, drv_usectohz(DELAY_DP_SETUP * 1000));

        mutex_exit(dps->dp_mutex);

#ifdef DEBUG_ERROR_INJECTION

        erri_test_number = ddi_prop_get_int(DDI_DEV_T_ANY, rcs->dip,
            DDI_PROP_DONTPASS, "test-no", 0);
        erri_test_intrvl = ddi_prop_get_int(DDI_DEV_T_ANY, rcs->dip,
            DDI_PROP_DONTPASS, "test-interval", 0);
        erri_test_repeat = ddi_prop_get_int(DDI_DEV_T_ANY, rcs->dip,
            DDI_PROP_DONTPASS, "test-repeat", 0);

        erri_test_count = 0;


        cmn_err(CE_CONT, "error injection test: no=%d, intrvl=%d, rep=%d\n",
            erri_test_number, erri_test_intrvl, erri_test_repeat);
#endif

}

/*
 * termination of the data protocol (called from the detach routine)
 */
void
rmc_comm_dp_fini(struct rmc_comm_state *rcs)
{
        rmc_comm_dp_state_t *dps = &rcs->dp_state;
        timeout_id_t     tid_delay_ack;
        timeout_id_t     tid_link_setup;

        DPRINTF(rcs, DGEN, (CE_CONT,
            "stats: reset=%d nak=%d start=%d stack=%d retries=%d crcerr=%d\n",
            dps->reset_cnt, dps->nak_cnt, dps->start_cnt, dps->stack_cnt,
            dps->retries_cnt, dps->crcerr_cnt));

        /*
         * if any timer is running, must be terminated here!
         */
        mutex_enter(dps->dp_mutex);
        tid_delay_ack = dps->timer_link_setup;
        tid_link_setup = dps->timer_delay_ack;
        dps->timer_link_setup = (timeout_id_t)0;
        dps->timer_delay_ack = (timeout_id_t)0;
        mutex_exit(dps->dp_mutex);

        if (tid_delay_ack)
                (void) untimeout(tid_delay_ack);

        if (tid_link_setup)
                (void) untimeout(tid_link_setup);

        /*
         * cv variables termination
         */
        cv_destroy(dps->cv_ok_to_send);
        cv_destroy(dps->req_resp.cv_wait_reply);
}

/*
 * This is the low-level receiver handler. It's job is to find a complete
 * message from the incoming data stream, and once it finds one to pass it
 * on to the upper layer (message processing).
 * (it must have the dp_mutex)
 */
void
rmc_comm_dp_drecv(struct rmc_comm_state *rcs, uint8_t *buf, int buflen)
{
        rmc_comm_dp_state_t     *dps = &rcs->dp_state;
        dp_packet_t             *pkt = &dps->dp_packet;
        uint8_t                  quit;
        int                      count;
        int                      max;

        ASSERT(MUTEX_HELD(dps->dp_mutex));

        pkt->inbuf = buf;
        pkt->inbuflen = buflen;

        DPRINTF(rcs, DPKT, (CE_CONT, "drecv len=%d\n", buflen));

        while (pkt->inbuflen > 0) {
                switch (pkt->rx_state) {

                case WAITING_FOR_SYNC:
                        while ((pkt->inbuflen > 0) &&
                            (*pkt->inbuf != SYNC_CHAR) &&
                            (*pkt->inbuf != ESC_CHAR)) {

                                DPRINTF(rcs, DPKT,
                                    (CE_CONT, "not SYNC: %02x\n",
                                    (uchar_t)(*pkt->inbuf)));

                                pkt->inbuf++;
                                pkt->inbuflen--;
                        }

                        if (pkt->inbuflen > 0) {
                                if (*pkt->inbuf == SYNC_CHAR)
                                        pkt->rx_state = WAITING_FOR_HDR;
                                else if (*pkt->inbuf == ESC_CHAR)
                                        pkt->rx_state = WAITING_FOR_SYNC_ESC;
                        }
                        break;

                case WAITING_FOR_SYNC_ESC:
                        pkt->inbuf++;
                        pkt->inbuflen--;
                        pkt->rx_state = WAITING_FOR_SYNC;
                        break;

                case WAITING_FOR_HDR:
                        while ((pkt->inbuflen > 0) &&
                            (*pkt->inbuf == SYNC_CHAR)) {
                                pkt->inbuf++;
                                pkt->inbuflen--;
                        }

                        if (pkt->inbuflen <= 0)
                                break;

                        if (*pkt->inbuf == ESC_CHAR) {
                                /*
                                 * ESC as first char of header?
                                 * Impossible - start over!
                                 */
                                pkt->rx_state = WAITING_FOR_SYNC;
                                pkt->inbuf++;
                                pkt->inbuflen--;
                                break;
                        }

                        /* Get a buffer for this message. */
                        pkt->buf = dp_get_buffer(rcs, DP_RX_BUFFER);
                        if (pkt->buf == NULL) {
                                /* Out of buffers - drop this msg. */
                                pkt->rx_state = WAITING_FOR_SYNC;
                                break;
                        }
                        DPRINTF(rcs, DPKT, (CE_CONT, "drecv first char %x\n",
                            (uchar_t)*pkt->inbuf));

                        pkt->buf[1] = *pkt->inbuf;
                        pkt->bufpos = 2;
                        pkt->rx_state = RECEIVING_HDR;

                        pkt->inbuf++;
                        pkt->inbuflen--;
                        break;

                case RECEIVING_HDR:
                        quit = 0;
                        while ((pkt->inbuflen > 0) &&
                            (*pkt->inbuf != SYNC_CHAR) &&
                            (*pkt->inbuf != ESC_CHAR)) {
                                pkt->buf[pkt->bufpos++] = *pkt->inbuf;
                                pkt->inbuf++;
                                pkt->inbuflen--;
                                if (pkt->bufpos >= sizeof (dp_header_t)) {
                                        dp_got_full_hdr(rcs, pkt);
                                        quit = 1;
                                        break;
                                } else if ((pkt->bufpos >= sizeof (bp_msg_t)) &&
                                    (IS_BOOT_MSG(pkt->buf[1]))) {
                                        dp_got_bp_msg(rcs, pkt);
                                        quit = 1;
                                        break;
                                }
                        }

                        if (quit)
                                break;

                        if (pkt->inbuflen > 0) {
                                /* Must have gotten an ESC_CHAR or SYNC_CHAR. */
                                if (*pkt->inbuf == SYNC_CHAR) {

                                        DPRINTF(rcs, DPKT,
                                                (CE_CONT, "drecv sync in hdr, "
                                                "bufpos=%d\n", pkt->bufpos));

                                        dp_release_buffer(rcs, DP_RX_BUFFER);
                                        pkt->buf = NULL;
                                        pkt->rx_state = WAITING_FOR_HDR;
                                } else {
                                        pkt->rx_state = RECEIVING_HDR_ESC;
                                }
                                pkt->inbuf++;
                                pkt->inbuflen--;
                        }
                        break;

                case RECEIVING_HDR_ESC:
                        pkt->buf[pkt->bufpos++] = *pkt->inbuf;
                        pkt->inbuf++;
                        pkt->inbuflen--;
                        if (pkt->bufpos >= sizeof (dp_header_t)) {
                                dp_got_full_hdr(rcs, pkt);
                        } else if ((pkt->bufpos >= sizeof (bp_msg_t)) &&
                            (IS_BOOT_MSG(pkt->buf[1]))) {
                                dp_got_bp_msg(rcs, pkt);
                        } else {
                                pkt->rx_state = RECEIVING_HDR;
                        }
                        break;

                case RECEIVING_BODY:
                        max = pkt->full_length - pkt->bufpos;
                        if (max > pkt->inbuflen)
                                max = pkt->inbuflen;

                        for (count = 0; count < max; count++)
                                if ((pkt->inbuf[count] == SYNC_CHAR) ||
                                    (pkt->inbuf[count] == ESC_CHAR))
                                        break;

                        if (count > 0) {
                                bcopy(pkt->inbuf, pkt->buf + pkt->bufpos,
                                    count);
                                pkt->inbuf += count;
                                pkt->inbuflen -= count;
                                pkt->bufpos += count;

                                if (pkt->bufpos >= pkt->full_length) {
                                        dp_got_full_msg(rcs, pkt);
                                        break;
                                }
                        }

                        if (count < max) {
                                /* Must have gotten an ESC_CHAR or SYNC_CHAR. */
                                if (*pkt->inbuf == SYNC_CHAR) {
                                        dp_release_buffer(rcs, DP_RX_BUFFER);
                                        pkt->buf = NULL;
                                        pkt->rx_state = WAITING_FOR_HDR;
                                } else {
                                        pkt->rx_state = RECEIVING_BODY_ESC;
                                }
                                pkt->inbuf++;
                                pkt->inbuflen--;
                        }
                        break;

                case RECEIVING_BODY_ESC:
                        pkt->buf[pkt->bufpos] = *pkt->inbuf;
                        pkt->inbuf++;
                        pkt->inbuflen--;
                        pkt->bufpos++;
                        if (pkt->bufpos >= pkt->full_length) {
                                dp_got_full_msg(rcs, pkt);
                        } else {
                                pkt->rx_state = RECEIVING_BODY;
                        }
                        break;
                }
        }
}

/*
 * Handle an incoming message. CRCs have been already checked so message
 * is good. check if sequence numbers are ok.
 * Handles: control message, asynchronous notification, reply to requests
 * and notify the leaf driver of those events.
 * (it must have the dp_mutex)
 */
void
rmc_comm_dp_mrecv(struct rmc_comm_state *rcs, uint8_t *buf)
{
        rmc_comm_dp_state_t *dps = &rcs->dp_state;
        dp_header_t *dp_msgp;
        uint8_t *datap;
        int datalen;
        dp_msg_intr_t *dmi = &dps->msg_intr;
        dp_req_resp_t *drr = &dps->req_resp;

        ASSERT(MUTEX_HELD(dps->dp_mutex));

        dp_msgp = (dp_header_t *)buf;

        datalen = dp_msgp->length -
            (sizeof (dp_header_t) - sizeof (dp_msgp->pad));

        if (datalen > 0) {
                datalen = datalen - sizeof (uint16_t); /* don't count msg CRC */
                datap = buf + sizeof (dp_header_t);
        } else {
                datap = NULL;
        }

        DPRINTF(rcs, DPRO, (CE_CONT,
            "[t%03dr%03d] mrecv msgtype: %02x, len=%d\n",
            dp_msgp->txnum, dp_msgp->rxnum, dp_msgp->type, datalen));

        /*
         * Handle control messages first
         */
        if (IS_UNNUMBERED_MSG(dp_msgp->type)) {
                switch (dp_msgp->type) {
                case DP_CTL_START:
                        /*
                         * CTL:start
                         * Re-init protocol processing.
                         * Enable data link
                         * Stop data link setup timer if running
                         */
                        DPRINTF(rcs, DPRO, (CE_CONT, "mrecv data link ok\n"));

                        dp_reset(rcs, dp_msgp->txnum, 1, 0);

                        dp_wake_up_waiter(rcs, 0);

                        /* Send CTL:stack message. */
                        (void) rmc_comm_dp_ctlsend(rcs, DP_CTL_STACK);

                        dps->start_cnt++;

                        dp_enable_data_link(rcs);

                        break;

                case DP_CTL_STACK:
                        /*
                         * CTL:stack
                         * Enable data link
                         * Stop data link setup timer if running
                         */
                        DPRINTF(rcs, DPRO, (CE_CONT, "mrecv data link ok\n"));

                        dp_reset(rcs, dp_msgp->txnum, 0, 0);

                        dp_wake_up_waiter(rcs, 0);

                        dps->stack_cnt++;

                        dp_enable_data_link(rcs);
                        break;

                case DP_CTL_RESPOND:
                        /*
                         * CTL:respond (heartbeat)
                         * Send a CTL:ack.
                         */
                        if (dps->data_link_ok) {
                                (void) rmc_comm_dp_ctlsend(rcs, DP_CTL_ACK);
                        }
                        break;

                case DP_CTL_ACK:
                        /*
                         * CTL:ack
                         * Call a transmit-side routine to handle it.
                         */
                        dp_tx_handle_ack(rcs, dp_msgp->rxnum);
                        break;

                case DP_CTL_NAK:
                        /*
                         * CTL:nak
                         * Call a transmit-side routine to handle it.
                         */
                        dp_tx_handle_nak(rcs, dp_msgp->rxnum);
                        break;

                default:
                        /* Drop message. */
                        DPRINTF(rcs, DPRO,
                            (CE_CONT, "mrecv unknown ctrlmsg\n"));
                        break;
                }
                return;
        }

        /*
         * Before processing the received message (NUMBERED), check that the
         * data link protocol is up. If not, ignore this message
         */
        if (!dps->data_link_ok) {
                DPRINTF(rcs, DPRO, (CE_CONT, "mrecv drop msg: no data link\n"));
                return;
        }

        /*
         * we received a message (NUMBERED) and data link is ok.
         * First, instead of ACKing this message now, we delay it. The reason
         * why is that a message can be sent (from this side) in the meantime
         * and it can ACK the received message (it will spare us to send
         * the ACK message across the wire).
         */

        /*
         * Handle acknowledgements even if this is a duplicate message.
         */
        if (dps->timer_delay_ack == (timeout_id_t)0) {
                dps->timer_delay_ack = timeout(dp_delay_ack_tohandler,
                    (void *) rcs, drv_usectohz(TX_RETRY_TIME/2 * 1000));
                DPRINTF(rcs, DGEN, (CE_CONT, "mrecv start ack t/o %p\n",
                    dps->timer_delay_ack));
        }
        dp_tx_handle_ack(rcs, dp_msgp->rxnum);

        if (dp_msgp->txnum != NEXT_SEQID(dps->last_rx_seqid)) {
                /* Duplicate message - free it up & return. */
                DPRINTF(rcs, DPRO, (CE_CONT, "mrecv dup msg txnum=%03d\n",
                    dp_msgp->txnum));
                return;
        }
        dps->last_rx_seqid = dp_msgp->txnum;

#ifdef DEBUG_ERROR_INJECTION

        if ((erri_test_number == ERRI_SEND_CTL_STACK ||
            erri_test_number == ERRI_SEND_CTL_START) &&
            erri_test_repeat >= 0 &&
            erri_test_count++ > 0 && !(erri_test_count % erri_test_intrvl)) {

                if (erri_test_number == ERRI_SEND_CTL_STACK) {
                        (void) rmc_comm_dp_ctlsend(rcs, DP_CTL_STACK);

                } else if (erri_test_number == ERRI_SEND_CTL_START) {
                        (void) rmc_comm_dp_ctlsend(rcs, DP_CTL_START);

                }
                if (erri_test_repeat == 0)
                        erri_test_repeat--; /* will not repeat the test */
        }

#endif

        /*
         * At this point, we know this is a good message.  We've
         * checked checksums, message types, and sequence id's.
         */

        /*
         * First, check if a driver has register for this message
         * Second, check if this message is a reply to a request
         * Third, check to see if ALOM is telling us it doesn't
         * know about the command code.
         */

        if (dmi->intr_handler != NULL &&
            dmi->intr_msg_type == dp_msgp->type) {

                rmc_comm_msg_t  *msgi = (rmc_comm_msg_t *)dmi->intr_arg;

                DPRINTF(rcs, DPRO, (CE_CONT,
                    "mrecv process async msg len=%d, max=%d\n",
                    datalen, msgi->msg_len));
                /*
                 * process asynchronous notification only if the registered
                 * driver is not currently processing any other notification
                 */
                mutex_enter(dmi->intr_lock);
                if (dmi->intr_state == NULL ||
                    (dmi->intr_state != NULL &&
                    *(dmi->intr_state) == RMC_COMM_INTR_IDLE)) {
                        /*
                         * check that the buffer is big enough. do not want to
                         * cross boundaries here..
                         */
                        if (datalen <= msgi->msg_len) {
                                bcopy(datap, msgi->msg_buf, datalen);
                                msgi->msg_bytes = datalen;

                        } else {
                                msgi->msg_bytes = -1;
                        }
                        /*
                         * trigger soft intr. in any case.
                         * if message is too big, at least, the leaf driver
                         * will be notified (bytes returned will be -1)
                         */
                        ddi_trigger_softintr(dmi->intr_id);
                }
                mutex_exit(dmi->intr_lock);

        } else if ((drr->flags & MSG_SENT) != 0 &&
            drr->response.msg_type == dp_msgp->type) {

                DPRINTF(rcs, DPRO, (CE_CONT,
                    "mrecv process reply len=%d, max=%d\n",
                    datalen, drr->response.msg_bufsiz));

                /*
                 * check that the recv buffer is big enough.
                 */
                if (datalen <= drr->response.msg_bufsiz) {
                        bcopy(datap, drr->response.msg_buf, datalen);
                        drr->response.msg_msglen = datalen;
                        dp_wake_up_waiter(rcs, MSG_REPLY_RXED);
                } else {
                        drr->response.msg_msglen = -1;
                        dp_wake_up_waiter(rcs, MSG_REPLY_RXED);
                }
        } else if (dp_msgp->type == DP_INVCMD &&
            (drr->flags & MSG_SENT) != 0 &&
            ((dp_invcmd_t *)datap)->inv_type == drr->request.msg_type) {
                drr->error_status = RCEINVCMD;
                dp_wake_up_waiter(rcs, MSG_ERROR);
        }
}

/*
 * to send a control message (unnumbered message)
 * (it must have the dp_mutex)
 */
int
rmc_comm_dp_ctlsend(struct rmc_comm_state *rcs, uint8_t type)
{
        dp_message_t ctlmsg;
        int err = RCNOERR;

        ctlmsg.msg_type = type;
        ctlmsg.msg_buf = NULL;
        ctlmsg.msg_msglen = 0;

        err = rmc_comm_dp_msend(rcs, &ctlmsg);

        return (err);
}

/*
 * to send data to the remote party.
 *
 * NUMBERED messages carry payload data of variable size. A buffer is allocated
 * dynamically for the trasmission of data. NUMBERED message trasmission
 * data status is stored in the dp_state request_response data structure.
 * This because: data sent must be acknowledged, trasmission can be re-tried,
 * upper layer has to know the state/result of the trasmission. Upper layer has
 * to: initialize the data struct, send data (this function), read result,
 * clean up the data struct.
 *
 * UNUMBERED data are just only control command which do not carry any payload
 * A local buffer is used (ctlbuf) instead. UNNUMBERED message are transient
 * data which is sent once and not re-tried. It does not use the
 * request_response data structure
 *
 * (it must have the dp_mutex)
 */
int
rmc_comm_dp_msend(struct rmc_comm_state *rcs, dp_message_t *req)
{
        rmc_comm_dp_state_t     *dps = &rcs->dp_state;
        dp_req_resp_t           *drr = &dps->req_resp;
        dp_message_t            *pkt;
        dp_header_t             *dp_msgp;
        dp_message_t             ctl;
        dp_header_t              ctlbuf;
        uint16_t                 data_crc;
        timeout_id_t             timer_delay_ack = 0;
        char                     first_time = 0;

        ASSERT(MUTEX_HELD(dps->dp_mutex));

        DPRINTF(rcs, DPRO, (CE_CONT, "msend msgtype=%02x\n", req->msg_type));

        if (IS_NUMBERED_MSG(req->msg_type)) {
                /*
                 * if there was an error, just return the error.
                 * Otherwise if the message was already acknowledged
                 * (NUMBERED message) then, there is no need to (re)send it.
                 * just wait for an expected reply (hence, do not return an
                 * error)
                 */
                if ((drr->flags & MSG_ERROR) != 0) {

                        DPRINTF(rcs, DPRO, (CE_CONT,
                            "msg send error flag=%02x\n", drr->flags));
                        return (RCEGENERIC);

                } else if ((drr->flags & MSG_ACKED) != 0) {

                        DPRINTF(rcs, DPRO, (CE_CONT,
                            "msg already ACKed flag=%02x\n", drr->flags));
                        return (RCNOERR);

                } else if ((drr->flags & MSG_SENT) == 0) {

                        first_time = 1;
                }

                /*
                 * everything is ok. Now check that the data protocol is up
                 * and running: messages cannot be sent if the link is down.
                 */
                if (!dps->data_link_ok) {
                        DPRINTF(rcs, DPRO, (CE_CONT,
                            "msend: can't send msg - no data link\n"));

                        /*
                         * do not return error, since it can be retried
                         * later (hoping that the data link will come
                         * up, in the meantime)
                         */
                        return (RCNOERR);

                }
        } else {
                first_time = 1;
        }

        /*
         * if the message has never been sent (and, hence, it is the first
         * time), then prepare the protocol packet: allocate a buffer,
         * create the message header, copy the message body into the buffer and
         * calculate CRCs
         */
        if (first_time) {

                if (IS_NUMBERED_MSG(req->msg_type)) {

                        drr->retries_left = TX_RETRIES;

                        /*
                         * Check length of the message.
                         */
                        if (req->msg_msglen > DP_MAX_MSGLEN) {
                                DPRINTF(rcs, DPRO,
                                    (CE_CONT, "msend err: msg too big\n"));
                                return (RCEINVARG);
                        }

                        pkt = &drr->request;

                        /*
                         * check that the message buffer is not already
                         * in use (race condition). If so, return error
                         */
                        if (pkt->msg_buf != NULL) {
                                DPRINTF(rcs, DPRO, (CE_CONT,
                                    "msend err: buf already in use\n"));
                                return (RCENOMEM);
                        }

                        /*
                         * allocate a buffer for the protocol packet
                         */
                        if ((pkt->msg_buf = dp_get_buffer(rcs,
                            DP_TX_BUFFER)) == NULL) {
                                DPRINTF(rcs, DPRO, (CE_CONT,
                                    "msend err: no mem\n"));
                                return (RCENOMEM);
                        }
                        pkt->msg_bufsiz = DP_BUFFER_SIZE;

                        /*
                         * increment tx sequence number if sending a NUMBERED
                         * message
                         */
                        dps->last_tx_seqid = NEXT_SEQID(dps->last_tx_seqid);
                } else {
                        /*
                         * UNUMBERED messages (or control messages) do not
                         * carry any data and, hence, have a 'small' fixed size
                         * (the size of the header). In this case,
                         * a 'local' buffer (ctlbuf) is used.
                         */
                        pkt = &ctl;
                        pkt->msg_buf = (uint8_t *)&ctlbuf;
                        pkt->msg_bufsiz = sizeof (dp_header_t);
                }

#ifdef DEBUG_ERROR_INJECTION

                if (((erri_test_number == ERRI_RX_SEQ_NUMBER &&
                    IS_NUMBERED_MSG(req->msg_type)) ||
                    (erri_test_number == ERRI_CTL_RX_SEQ_NUMBER &&
                    IS_UNNUMBERED_MSG(req->msg_type))) &&
                    erri_test_repeat >= 0 &&
                    erri_test_count++ > 0 &&
                    !(erri_test_count % erri_test_intrvl)) {

                        dps->last_rx_seqid--;

                        if (erri_test_repeat == 0)
                                erri_test_repeat--; /* will not repeat it */
                }
#endif

                /*
                 * create the protocol packet
                 */
                pkt->msg_type = req->msg_type;

                /*
                 * length of the packet (including pad bytes)
                 */
                pkt->msg_msglen = req->msg_msglen + sizeof (dp_header_t);

                /*
                 * message header:
                 *  set the message type
                 *  set the length of the message (excluding pad bytes)
                 *  set tx/rx sequence numbers
                 *  calculate CRC
                 */
                dp_msgp = (dp_header_t *)pkt->msg_buf;
                dp_msgp->type = pkt->msg_type;

                if (req->msg_msglen == 0)
                        dp_msgp->length = pkt->msg_msglen -
                            sizeof (dp_msgp->pad);
                else
                        dp_msgp->length = sizeof (data_crc) +
                            pkt->msg_msglen - sizeof (dp_msgp->pad);

                dp_msgp->txnum = dps->last_tx_seqid;
                dp_msgp->rxnum = dps->last_rx_seqid;

                dp_msgp->crc = dp_calc_crc16(pkt->msg_buf +
                    sizeof (dp_msgp->pad), sizeof (dp_header_t) -
                    sizeof (dp_msgp->crc) - sizeof (dp_msgp->pad));

#ifdef DEBUG_ERROR_INJECTION

                if (((erri_test_number == ERRI_CRC_HEADER &&
                    IS_NUMBERED_MSG(pkt->msg_type)) ||
                    (erri_test_number == ERRI_CTL_CRC_HEADER &&
                    IS_UNNUMBERED_MSG(pkt->msg_type))) &&
                    erri_test_repeat >= 0 &&
                    erri_test_count++ > 0 &&
                    !(erri_test_count % erri_test_intrvl)) {

                        dp_msgp->crc = dp_msgp->crc/2;
                        if (erri_test_repeat == 0)
                                erri_test_repeat--; /* will not repeat it */
                }
#endif

                /*
                 * copy message body (if present) into the buffer
                 * and calculate message CRC
                 */
                if (req->msg_msglen > 0) {
                        bcopy(req->msg_buf, pkt->msg_buf + sizeof (dp_header_t),
                            req->msg_msglen);
                        data_crc = dp_calc_crc16(pkt->msg_buf +
                            sizeof (dp_header_t),
                            req->msg_msglen);

#ifdef DEBUG_ERROR_INJECTION

                        if (erri_test_number == ERRI_CRC_MSG &&
                            erri_test_repeat >= 0 &&
                            erri_test_count++ > 0 &&
                            !(erri_test_count % erri_test_intrvl)) {

                                data_crc = data_crc/2;
                                if (erri_test_repeat == 0)
                                        erri_test_repeat--;
                        }
#endif
                        bcopy((void *) &data_crc,
                            pkt->msg_buf + (sizeof (dp_header_t) +
                            req->msg_msglen),
                            sizeof (data_crc));
                }
        } else {
                /*
                 * message has already been sent (and packetized).
                 * get the message packet from the request/response
                 * data structure
                 */
                pkt = &drr->request;
                dp_msgp = (dp_header_t *)pkt->msg_buf;
                dps->retries_cnt++;
        }

        /*
         *  NUMBERED messages
         */
        if (IS_NUMBERED_MSG(pkt->msg_type)) {

                /*
                 * check that we have not exceeded the maximum number of
                 * retries
                 */
                if (drr->retries_left-- <= 0) {

                        drr->flags |= MSG_ERROR; /* set error flag */

                        /*
                         * restart the data protocol link
                         */
                        dp_reset(rcs, INITIAL_SEQID, 0, 1);

                        return (RCEMAXRETRIES);
                }

                if (dps->timer_delay_ack != (timeout_id_t)0) {
                        /*
                         * Cancel any pending acknowledgements - we're
                         * going to send a message which will include
                         * an acknowledgement.
                         */
                        timer_delay_ack = dps->timer_delay_ack;

                        /*
                         * the timer is actually removed at the end of this
                         * function since I need to release the dp_mutex.
                         * Instead I clear the timer variable so that the
                         * timeout callback will not do any processing in the
                         * meantime.
                         */
                        dps->timer_delay_ack = 0;
                }

                drr->flags |= MSG_SENT;
        }

        /*
         * set rx sequence number (as we might have received a message in the
         * meantime). tx sequence number to be the same (we can only send one
         * message per time)
         */
        if (dp_msgp->rxnum != dps->last_rx_seqid) {

                dp_msgp->rxnum = dps->last_rx_seqid;

                /*
                 * re-calculate CRC (header)
                 */
                dp_msgp->crc = dp_calc_crc16(pkt->msg_buf +
                    sizeof (dp_msgp->pad), sizeof (dp_header_t) -
                    sizeof (dp_msgp->crc) - sizeof (dp_msgp->pad));
        }

        DPRINTF(rcs, DPRO, (CE_CONT, "[t%03dr%03d] msend msgtype=%02x\n",
            dp_msgp->txnum, dp_msgp->rxnum, dp_msgp->type));

        /*
         * send this message
         */

        dp_send_packet(rcs, pkt->msg_buf);

        /*
         * remove delay ack timer (if any is running)
         * Note that the dp_mutex must be released before calling
         * untimeout. Otherwise we may have a deadlock situation.
         */
        if (timer_delay_ack != 0) {
                DPRINTF(rcs, DGEN, (CE_CONT, "msend remove ack timer %p\n",
                    timer_delay_ack));
                mutex_exit(dps->dp_mutex);
                (void) untimeout(timer_delay_ack);
                mutex_enter(dps->dp_mutex);
        }

        return (RCNOERR);
}

/*
 * to send a boot protocol message
 * (this is to support the firmware download feature)
 */
void
rmc_comm_bp_msend(struct rmc_comm_state *rcs, bp_msg_t *bp_msg)
{
        char syncbuf[2];

        ASSERT(MUTEX_HELD(rcs->dp_state.dp_mutex));

        DPRINTF(rcs, DPRO, (CE_CONT, "send bp msg: %02x %02x %02x\n",
            bp_msg->cmd, bp_msg->dat1, bp_msg->dat2));

        rcs->dp_state.req_resp.flags |= MSG_SENT_BP;

        /* First, send out two SYNC characters. */
        syncbuf[0] = syncbuf[1] = (char)SYNC_CHAR;
        rmc_comm_serdev_send(rcs, syncbuf, 2);

        /* Next, send the BP message. */
        rmc_comm_serdev_send(rcs, (char *)&bp_msg->cmd,
            sizeof (bp_msg_t) - sizeof (bp_msg->pad));
}

/*
 * to send a fw s-record
 * (this is to support the firmware download feature)
 */
void
rmc_comm_bp_srecsend(struct rmc_comm_state *rcs, char *buf, int buflen)
{
        ASSERT(MUTEX_HELD(rcs->dp_state.dp_mutex));

        rcs->dp_state.req_resp.flags |= MSG_SENT_BP;

        rmc_comm_serdev_send(rcs, buf, buflen);
}

/*
 * clean up a request/response session
 * (it must have the dp_mutex)
 */

void
rmc_comm_dp_mcleanup(struct rmc_comm_state *rcs)
{
        rmc_comm_dp_state_t *dps = &rcs->dp_state;
        dp_req_resp_t *drr = &dps->req_resp;
        dp_message_t *req = &drr->request;
        dp_message_t *resp = &drr->response;

        ASSERT(MUTEX_HELD(dps->dp_mutex));

        DPRINTF(rcs, DGEN, (CE_CONT, "msg cleanup\n"));

        /*
         * 'release' memory
         * memory is only 'dynamically allocated for NUMBERED messages
         */
        if (req->msg_buf != NULL)
                dp_release_buffer(rcs, DP_TX_BUFFER);

        drr->flags = 0;
        drr->error_status = 0;

        req->msg_type = DP_NULL_MSG;
        req->msg_buf = NULL;
        req->msg_msglen = 0;
        req->msg_bufsiz = 0;
        resp->msg_type = DP_NULL_MSG;
        resp->msg_buf = NULL;
        resp->msg_msglen = 0;
        resp->msg_bufsiz = 0;
}