/*
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* response to the RMC
*
*/
/*
* 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>
static int rmc_comm_wait_bp_reply(struct rmc_comm_state *,
static void rmc_comm_wait_enable_to_send(struct rmc_comm_state *,
static void rmc_comm_wake_up_next(struct rmc_comm_state *);
/*
* leaf driver to use this function to send a request to the remote side (RMC)
* and wait for a reply
*/
int
{
int err;
/*
* get the soft state struct (instance 0)
*/
"rmc_comm_request_response")) == NULL)
return (RCENOSOFTSTATE);
do {
} while (err == RCEGENERIC);
return (err);
}
/*
* leaf driver to use this function to send a request to the remote side (RMC)
* without waiting for a reply. If flag is RMC_COMM_DREQ_URGENT, the request
* message is sent once-off (an eventual pending request is aborted). This
* flag must only be used when try to send a request in critical condition
* (while the system is shutting down for instance and the CPU signature
* has to be sent). Otherwise, the request is stored in a temporary location
* and delivered by a thread.
*/
int
{
/*
* get the soft state struct (instance 0)
*/
"rmc_comm_request_response")) == NULL)
return (RCENOSOFTSTATE);
/*
* just a sanity check...
*/
return (RCEINVARG);
}
"reqnowait, ctrl msg not allowed! req type=%x\n",
return (RCEINVARG);
}
if (flag == RMC_COMM_DREQ_URGENT) {
/*
* Send this request with high priority i.e. abort eventual
*/
/*
* Handle the case where we are called during panic
* processing. If that occurs, then another thread in
* rmc_comm might have been idled by panic() while
* holding dp_mutex. As a result, do not unconditionally
* grab dp_mutex.
*/
if (ddi_in_panic() != 0) {
return (RCENODATALINK);
}
} else {
}
/*
* send the request only if the protocol data link is up.
* it is pointless to send it in the other case.
*/
if (dps->data_link_ok) {
/*
* (save its current status)
*/
if (dps->pending_request) {
}
/*
* send the request message
*/
/*
* wait for fifos to drain
*/
/*
* clean up the current session
*/
/*
* abort an old session (if any)
*/
if (dps->pending_request) {
}
}
} else {
/*
* Get an 'independent' thread (rmc_comm_send_pend_req)
* to send this request (since the calling thread does not
* want to wait). Copy the request in the drvintf state
* structure and signal the thread.
*/
/*
* copy the request in a temporary location
* (drvinf_state structure) and signal the thread
* that a request message has to be delivered
*/
} else {
/*
* not enough space to hold the request
*/
err = RCEREPTOOBIG;
}
} else {
/*
* only one request per time can be processed.
* the thread is either busy (RMC_COMM_DREQ_ST_PROCESS)
* or terminating (RMC_COMM_DREQ_ST_EXIT)
*/
err = RCEGENERIC;
}
}
return (err);
}
/*
* Function used to send a request and (eventually) wait for a response.
* It can be called from a leaf driver (via rmc_comm_request_response) or
* from the thread in charge of sending 'no-wait' requests
* (rmc_comm_send_pend_req).
*/
static int
{
int err;
/*
* just a sanity check...
*/
return (RCEINVARG);
}
/*
* drivers cannot send control messages at all. They are meant to
* be used at low level only.
*/
"reqresp, ctrl msg not allowed! req type=%x\n",
return (RCEINVARG);
}
/*
* Handle the case where we are called during panic
* processing. If that occurs, then another thread in
* rmc_comm might have been idled by panic() while
* holding dp_mutex. As a result, do not unconditionally
* grab dp_mutex.
*/
if (ddi_in_panic() != 0) {
return (RCENODATALINK);
}
} else {
}
/*
* if the data link set up is suspended, just return.
* the only time that this can happen is during firmware download
* (see rmc_comm_request_response_bp). Basically, the data link is
* down and the timer for setting up the data link is not running.
*/
if (!dps->data_link_ok &&
return (RCENODATALINK);
}
/*
* We now have control of the RMC.
* Place a lower limit on the shortest amount of time to be
* waited before timing out while communicating with the RMC
*/
if (wait_time < DP_MIN_TIMEOUT)
/*
*/
drr->error_status = 0;
/*
* set the 'expected reply' buffer: get the buffer already allocated
* for the response (if a reply is expected!)
*/
} else {
}
/*
* send the request message
*/
/*
* send the message and wait for the reply or ACKnowledgment
* timeframe defined
*/
/*
* wait for a reply or an acknowledgement
*/
"reqresp send status: flags=%02x req=%x resp=%x tick=%ld\n",
stop_clockt - resend_clockt));
/*
* Check for error condition first
* Then, check if it has timeout and if there is any
* time left to resend the message.
*/
if (drr->error_status == 0) {
err = RCEGENERIC;
} else {
}
break;
/*
* yes! here is the reply
*/
/*
* get the actual length of the msg
* a negative value means that the reply message
* was too big for the receiver buffer
*/
err = RCEREPTOOBIG;
else
break;
/*
* yes! message has been acknowledged
*/
break;
} else if ((stop_clockt - resend_clockt) <= 0) {
/*
* no more time left. set the error code,
* exit the loop
*/
err = RCETIMEOUT;
break;
}
}
return (err);
}
/*
* Function used to send a BP (Boot Prom) message and get the reply.
* BP protocol is provided only to support firmware download.
*
* This function will look for the following key BP protocol commands:
* sessions cannot be started. The reason why is that this command will cause
* RMC fw to jump to the boot monitor (BOOTMON_FLASH) and data protocol is not
* operational. In this context, RMC fw will only be using the BP protocol.
* BP_OBP_RESET: data link setup timer is resumed. This command cause the RMC
* to reboot and hence become operational.
*/
int
{
/*
* get the soft state struct (instance 0)
*/
"rmc_comm_request_response_bp")) == NULL)
return (RCENOSOFTSTATE);
/*
* sanity check: request_bp buffer must always be provided
*/
if (request_bp == NULL) {
return (RCEINVARG);
}
/*
* only BP message can be sent
*/
"reqresp_bp, only BP msg are allowed! type=%x\n",
return (RCEINVARG);
}
/*
* Now, before sending the message, just check what it is being sent
* and take action accordingly.
*
* is it BP_OBP_BOOTINIT or BP_OBP_RESET command?
*/
/*
* bring down the protocol data link
*/
dps->data_link_ok = 0;
bootinit_sent = 1;
/*
* restart the data link set up timer. RMC is coming up...
*/
}
/*
*/
drr->error_status = 0;
/*
* set the reply buffer: get the buffer already allocated
* for the response
*/
if (response_bp != NULL) {
response_bp->msg_len));
}
/*
* send the BP message and wait for the reply
*/
if (response_bp != NULL) {
/*
* place a lower limit on the shortest amount of time to be
* waited before timing out while communicating with the RMC
*/
if (wait_time < DP_MIN_TIMEOUT)
stop_clockt)) == RCNOERR) {
/*
* get the actual length of the msg
* a negative value means that the reply message
* was too big for the receiver buffer
*/
if (response_bp->msg_bytes < 0) {
err = RCEREPTOOBIG;
} else if (bootinit_sent) {
/*
* BOOTINIT cmd may fail. In this is the case,
* the RMC is still operational. Hence, we
* try (once) to set up the data link
* protocol.
*/
(void) rmc_comm_dp_ctlsend(rcs,
}
}
}
}
return (err);
}
/*
* to register for an asynchronous (via soft interrupt) notification
* of a message from the remote side (RMC)
*/
int
{
return (RCENOSOFTSTATE);
/*
* lock is required. If it is not defined, the
* interrupt handler routine cannot be registered.
*/
return (RCEINVARG);
}
/*
* only one interrupt handler can be registered.
*/
} else {
}
} else {
err = RCEALREADYREG;
}
return (err);
}
/*
* To unregister for asynchronous notifications
*/
int
{
return (RCENOSOFTSTATE);
msgintr->intr_msg_type = 0;
} else {
err = RCEGENERIC;
}
return (err);
}
/*
* To send raw data (firmware s-records) down to the RMC.
* It is provided only to support firmware download.
*/
int
{
int err;
/*
* get the soft state struct (instance 0)
*/
"rmc_comm_request_response_bp")) == NULL)
return (RCENOSOFTSTATE);
/*
* sanity check: response_bp buffer must always be provided
*/
return (RCEINVARG);
}
/*
*/
drr->error_status = 0;
/*
* set the reply buffer: get the buffer already allocated
* for the response
*/
/*
* send raw data (s-record) and wait for the reply (BP message)
*/
/*
* place a lower limit on the shortest amount of time to be
* waited before timing out while communicating with the RMC
*/
if (wait_time < DP_MIN_TIMEOUT)
stop_clockt)) == RCNOERR) {
/*
* get the actual length of the msg
* a negative value means that the reply message
* was too big for the receiver buffer
*/
if (response_bp->msg_bytes < 0) {
err = RCEREPTOOBIG;
}
}
return (err);
}
/*
* To wait for (any) BP message to be received.
* (dp_mutex must be held)
*/
static int
{
"reqresp_bp, send: flags=%02x, clktick left=%ld\n",
/*
* Check for error condition first.
* Then, check if it has timeout.
* Then, check if the command has been replied.
*/
err = RCEGENERIC;
} else if (clockleft <= 0) {
/*
* timeout
*/
err = RCETIMEOUT;
err = RCEGENERIC;
}
return (err);
}
/*
* Wait for the pending_request flag to be cleared and acquire it for our
* session with the RMC.
* Note that all send-receive actions to the RMC include a time-out, so
* the pending-request must eventually go away - even if the RMC is down.
* Hence there is no need to timeout the wait action of this function.
* (dp_mutex must be held on entry).
*/
static void
{
dps->pending_request));
/*
* A new message can actually grab the lock before the thread
* that has just been signaled. Therefore, we need to double
* check to make sure that pending_request is not already set
* after we wake up.
*
* Potentially this could mean starvation for certain unfortunate
* threads that keep getting woken up and putting back to sleep.
* But the window of such contention is very small to begin with.
*/
while (dps->pending_request) {
/*
* just 'sit and wait' until there are no pending requests
*/
}
/*
* else will be able to send anything.
*/
}
/*
* To wake up one of the threads (if any) waiting for starting a
* (dp_mutex must be held)
*/
static void
{
/*
* wake up eventual waiting threads...
*/
}
/*
* thread which delivers pending request message to the rmc. Some leaf drivers
* message is stored temporarily in the state structure and this thread
* gets woken up to deliver it.
*/
static void
{
thread_exit();
/* NOTREACHED */
}
"rmc_comm_send_pend_req");
for (;;) {
/*
* Wait for someone to tell me to continue.
*/
}
/* RMC_COMM_DREQ_ST_EXIT implies signal by _detach(). */
/* dis->dreq_mutex is held at this point! */
thread_exit();
/* NOTREACHED */
}
/*
* deliver the request (and wait...)
*/
}
}
}
/*
* start thread to deal with pending requests to be delivered asynchronously
*/
static int
{
int err = 0;
}
return (err);
}
/*
* stop the thread (to deliver pending request messages)
*/
static void
{
if (tid != 0) {
}
/*
* Wait for rmc_comm_send_pend_req() to finish
*/
if (tid != 0)
}
/*
* init function - start thread to deal with pending requests (no-wait requests)
*/
int
{
int err = 0;
if (err != 0) {
}
return (err);
}
/*
* fini function - kill thread to deal with pending requests (no-wait requests)
*/
void
{
}