/*
* 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
* 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/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
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 void dp_init_buffers(struct rmc_comm_state *);
static void dp_enable_data_link(struct rmc_comm_state *);
/*
* utilities...
*/
/*
*/
static void
{
int i;
for (i = 0; i < DP_BUFFER_COUNT; i++)
}
/*
*/
static uint8_t *
{
(CE_CONT, "get buffer err. type=%d, in_use=%d\n",
return (NULL);
}
}
/*
*/
static void
{
(CE_CONT, "free buffer err. type=%d, in_use=%d\n",
return;
}
}
/*
* setup data link timeout handler
* (called without having the dp_mutex)
*/
static void
{
/*
* check if timer has actually been cancelled
*/
/*
* send CTL:start to the remote side to set up the data link
*/
}
}
/*
* delay acknowledgment of a received message timeout handler
* (called without having the dp_mutex)
*/
static void
{
#ifdef DEBUG_ERROR_INJECTION
if (erri_test_number == ERRI_ACK_MSG &&
erri_test_repeat >= 0 &&
/*
* DON'T ACK THE MESSAGE - BE SILENT!
*/
if (erri_test_repeat == 0)
erri_test_repeat--; /* will not repeat the test */
return;
}
#endif
/*
* check if timer has actually been cancelled
*/
/*
* ACK the message
*/
}
}
/*
* Enable data link protocol:
* stop data link setup timer
* set data_link_ok flag
* (must already have the dp_mutex)
*/
static void
{
if (timer_id != (timeout_id_t)0) {
}
}
/*
* CRC calculation routine.
*/
static uint16_t
{
crc = 0;
while (len--) {
}
return (crc);
}
/*
* Reset the data protocol
* (dp_mutex must be held)
*/
void
{
"reset proto: rxsid=%d, flushtx=%d, restartdp=%d\n",
"stats: reset=%d nak=%d start=%d stack=%d retries=%d crcerr=%d\n",
/*
* Flush pending tx message.
*/
if (flush_tx) {
/*
* then just abort it.
*/
}
/*
* restart data link, but only if the data link set up timer is
* not already running.
*/
dps->data_link_ok = 0;
/*
* set up the data protocol link
*/
}
}
/*
* Handles acknowledgment of a message previously sent OR a heartbeat command
* (CTL_RESPOND).
*/
static void
{
/*
* no pending messages, so nothing to do
*/
return;
}
/*
* message was sent and acknowledged successfully
* set flag and signal the waiting task if it is not
* expecting a reply back
*/
}
}
}
/*
* Handles NAK
*/
static void
{
/*
* 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.
*/
}
/*
* Got a full header. Check header CRC and get the length of the packet
*/
static void
{
/*
* 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));
if ((pkt->full_length < 0) ||
/*
* not a valid message: either message too big or too small
*/
/*
* process message: it is basically a control message
* (no data being carried)
*/
} else {
}
}
/*
* 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
{
/*
* ignore BP message, if it is not expected
*/
/*
* A boot prom (BP) msg has been sent. Here is the
* 'expected' reply
*/
/*
* check that the recv buffer is big enough (just in case).
*/
} else {
}
}
/* Return the buffer to the pool and wait for the next msg. */
}
/*
* Got a complete message, check CRC and pass it on to the upper layer (message
* processing)
*/
static void
{
int msglen;
/*
* check message CRC
*/
/*
* CRC is ok, process this message
*/
} else {
}
}
/*
* Check the checksum of the header & return the length field. If the
* checksum check fails, then return -1.
*/
static int
{
} else {
return (-1);
}
}
/*
* to send a protocol packet to the remote side. it handles escaping SYNC
* and ESC chars
*/
static void
{
/* First, send out two SYNC characters. */
while (total > 0) {
cur = 0;
/* Count up characters that don't need ESC'ing. */
cur++;
}
/* Send characters that don't need escaping, if any. */
if (cur > 0) {
}
/*
* If total > 0 at this point, we need to send an
* ESC'd character. Send as many as there are.
*/
while ((total > 0) &&
buf++;
total--;
}
}
}
/*
* session.
*/
void
{
}
}
/*
* initialization of the data protocol (called from the attach routine)
*/
void
{
/*
* initialize data structure:
*/
/*
* initialize packet receive handler state
*/
/*
* cv variables initialization
* (dp_mutex has been already created during the serial device
* initialization)
*/
/*
* initialize the data protocol (reset sequence numbers, etc.)
*/
/*
* start timer to 'delay' the set up of the data protocol link
*/
#ifdef DEBUG_ERROR_INJECTION
DDI_PROP_DONTPASS, "test-no", 0);
DDI_PROP_DONTPASS, "test-interval", 0);
DDI_PROP_DONTPASS, "test-repeat", 0);
erri_test_count = 0;
#endif
}
/*
* termination of the data protocol (called from the detach routine)
*/
void
{
"stats: reset=%d nak=%d start=%d stack=%d retries=%d crcerr=%d\n",
/*
* if any timer is running, must be terminated here!
*/
if (tid_delay_ack)
(void) untimeout(tid_delay_ack);
if (tid_link_setup)
(void) untimeout(tid_link_setup);
/*
* cv variables termination
*/
}
/*
* 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
{
int count;
int max;
case WAITING_FOR_SYNC:
(CE_CONT, "not SYNC: %02x\n",
}
}
break;
case WAITING_FOR_SYNC_ESC:
break;
case WAITING_FOR_HDR:
}
break;
/*
* ESC as first char of header?
* Impossible - start over!
*/
break;
}
/* Get a buffer for this message. */
/* Out of buffers - drop this msg. */
break;
}
break;
case RECEIVING_HDR:
quit = 0;
quit = 1;
break;
quit = 1;
break;
}
}
if (quit)
break;
/* Must have gotten an ESC_CHAR or SYNC_CHAR. */
(CE_CONT, "drecv sync in hdr, "
} else {
}
}
break;
case RECEIVING_HDR_ESC:
} else {
}
break;
case RECEIVING_BODY:
break;
if (count > 0) {
count);
break;
}
}
/* Must have gotten an ESC_CHAR or SYNC_CHAR. */
} else {
}
}
break;
case RECEIVING_BODY_ESC:
} else {
}
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
{
int datalen;
if (datalen > 0) {
} else {
}
"[t%03dr%03d] mrecv msgtype: %02x, len=%d\n",
/*
* Handle control messages first
*/
case DP_CTL_START:
/*
* CTL:start
* Re-init protocol processing.
* Enable data link
* Stop data link setup timer if running
*/
dp_wake_up_waiter(rcs, 0);
/* Send CTL:stack message. */
break;
case DP_CTL_STACK:
/*
* CTL:stack
* Enable data link
* Stop data link setup timer if running
*/
dp_wake_up_waiter(rcs, 0);
break;
case DP_CTL_RESPOND:
/*
* CTL:respond (heartbeat)
* Send a CTL:ack.
*/
if (dps->data_link_ok) {
}
break;
case DP_CTL_ACK:
/*
* CTL:ack
* Call a transmit-side routine to handle it.
*/
break;
case DP_CTL_NAK:
/*
* CTL:nak
* Call a transmit-side routine to handle it.
*/
break;
default:
/* Drop message. */
(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) {
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.
*/
dps->timer_delay_ack));
}
/* Duplicate message - free it up & return. */
return;
}
#ifdef DEBUG_ERROR_INJECTION
if ((erri_test_number == ERRI_SEND_CTL_STACK ||
erri_test_repeat >= 0 &&
if (erri_test_number == ERRI_SEND_CTL_STACK) {
} else if (erri_test_number == ERRI_SEND_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.
*/
"mrecv process async msg len=%d, max=%d\n",
/*
* process asynchronous notification only if the registered
* driver is not currently processing any other notification
*/
/*
* check that the buffer is big enough. do not want to
* cross boundaries here..
*/
} else {
}
/*
* trigger soft intr. in any case.
* if message is too big, at least, the leaf driver
* will be notified (bytes returned will be -1)
*/
}
"mrecv process reply len=%d, max=%d\n",
/*
* check that the recv buffer is big enough.
*/
} else {
}
}
}
/*
* to send a control message (unnumbered message)
* (it must have the dp_mutex)
*/
int
{
ctlmsg.msg_msglen = 0;
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,
* 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
{
char first_time = 0;
/*
* 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)
*/
return (RCEGENERIC);
return (RCNOERR);
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) {
"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) {
/*
* Check length of the message.
*/
(CE_CONT, "msend err: msg too big\n"));
return (RCEINVARG);
}
/*
* check that the message buffer is not already
* in use (race condition). If so, return error
*/
"msend err: buf already in use\n"));
return (RCENOMEM);
}
/*
* allocate a buffer for the protocol packet
*/
DP_TX_BUFFER)) == NULL) {
"msend err: no mem\n"));
return (RCENOMEM);
}
/*
* increment tx sequence number if sending a NUMBERED
* message
*/
} 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.
*/
}
#ifdef DEBUG_ERROR_INJECTION
if (((erri_test_number == ERRI_RX_SEQ_NUMBER &&
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
*/
/*
* length of the packet (including pad bytes)
*/
/*
* message header:
* set the message type
* set the length of the message (excluding pad bytes)
* calculate CRC
*/
if (req->msg_msglen == 0)
else
#ifdef DEBUG_ERROR_INJECTION
if (((erri_test_number == ERRI_CRC_HEADER &&
erri_test_repeat >= 0 &&
erri_test_count++ > 0 &&
!(erri_test_count % erri_test_intrvl)) {
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) {
req->msg_msglen);
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)) {
if (erri_test_repeat == 0)
}
#endif
req->msg_msglen),
sizeof (data_crc));
}
} else {
/*
* message has already been sent (and packetized).
* data structure
*/
dps->retries_cnt++;
}
/*
* NUMBERED messages
*/
/*
* check that we have not exceeded the maximum number of
* retries
*/
if (drr->retries_left-- <= 0) {
/*
* restart the data protocol link
*/
return (RCEMAXRETRIES);
}
/*
* Cancel any pending acknowledgements - we're
* going to send a message which will include
* an acknowledgement.
*/
/*
* 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;
}
}
/*
* 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)
*/
/*
* re-calculate CRC (header)
*/
}
/*
* send this message
*/
/*
* 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) {
(void) untimeout(timer_delay_ack);
}
return (RCNOERR);
}
/*
* to send a boot protocol message
* (this is to support the firmware download feature)
*/
void
{
/* First, send out two SYNC characters. */
/* Next, send the BP message. */
}
/*
* to send a fw s-record
* (this is to support the firmware download feature)
*/
void
{
}
/*
* (it must have the dp_mutex)
*/
void
{
/*
* 'release' memory
* memory is only 'dynamically allocated for NUMBERED messages
*/
drr->error_status = 0;
req->msg_msglen = 0;
req->msg_bufsiz = 0;
resp->msg_msglen = 0;
resp->msg_bufsiz = 0;
}