#include <sys/conf.h>
#include <sys/callb.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/disp.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>
void dp_reset(struct rmc_comm_state *, uint8_t, boolean_t, boolean_t);
void dp_wake_up_waiter(struct rmc_comm_state *, uint8_t);
static int rmc_comm_send_req_resp(struct rmc_comm_state *rcs,
rmc_comm_msg_t *request, rmc_comm_msg_t *response, uint32_t wait_time);
static int rmc_comm_wait_bp_reply(struct rmc_comm_state *,
rmc_comm_dp_state_t *, dp_req_resp_t *, clock_t);
static void rmc_comm_wait_enable_to_send(struct rmc_comm_state *,
rmc_comm_dp_state_t *);
static void rmc_comm_wake_up_next(struct rmc_comm_state *);
static void rmc_comm_send_pend_req(caddr_t arg);
static int rmc_comm_dreq_thread_start(struct rmc_comm_state *rcs);
static void rmc_comm_dreq_thread_kill(struct rmc_comm_state *rcs);
int
rmc_comm_request_response(rmc_comm_msg_t *request,
rmc_comm_msg_t *response, uint32_t wait_time)
{
struct rmc_comm_state *rcs;
int err;
if ((rcs = rmc_comm_getstate(NULL, 0,
"rmc_comm_request_response")) == NULL)
return (RCENOSOFTSTATE);
do {
err = rmc_comm_send_req_resp(rcs, request, response, wait_time);
} while (err == RCEGENERIC);
return (err);
}
int
rmc_comm_request_nowait(rmc_comm_msg_t *request, uint8_t flag)
{
struct rmc_comm_state *rcs;
rmc_comm_dp_state_t *dps;
rmc_comm_drvintf_state_t *dis;
dp_message_t req;
int err = RCNOERR;
uint8_t flags = 0;
if ((rcs = rmc_comm_getstate(NULL, 0,
"rmc_comm_request_response")) == NULL)
return (RCENOSOFTSTATE);
if (request == NULL) {
DPRINTF(rcs, DAPI, (CE_CONT, "reqnowait, invalid args\n"));
return (RCEINVARG);
}
if (!IS_NUMBERED_MSG(request->msg_type)) {
DPRINTF(rcs, DAPI, (CE_CONT,
"reqnowait, ctrl msg not allowed! req type=%x\n",
request->msg_type));
return (RCEINVARG);
}
if (flag == RMC_COMM_DREQ_URGENT) {
dps = &rcs->dp_state;
DPRINTF(rcs, DAPI, (CE_CONT, "going to send request=%x (URG)\n",
request->msg_type));
if (ddi_in_panic() != 0) {
if (mutex_tryenter(dps->dp_mutex) == 0) {
return (RCENODATALINK);
}
} else {
mutex_enter(dps->dp_mutex);
}
if (dps->data_link_ok) {
if (dps->pending_request) {
flags = dps->req_resp.flags;
rmc_comm_dp_mcleanup(rcs);
}
req.msg_type = request->msg_type;
req.msg_buf = (uint8_t *)request->msg_buf;
req.msg_msglen = (uint16_t)request->msg_len;
DPRINTF(rcs, DAPI, (CE_CONT, "send request=%x (URG)\n",
request->msg_type));
err = rmc_comm_dp_msend(rcs, &req);
rmc_comm_serdev_drain(rcs);
rmc_comm_dp_mcleanup(rcs);
if (dps->pending_request) {
dps->req_resp.flags = flags;
dp_wake_up_waiter(rcs, MSG_ERROR);
}
}
mutex_exit(dps->dp_mutex);
} else {
dis = &rcs->drvi_state;
mutex_enter(dis->dreq_mutex);
if (dis->dreq_state == RMC_COMM_DREQ_ST_WAIT) {
DPRINTF(rcs, DAPI, (CE_CONT, "get to send request=%x\n",
request->msg_type));
if (request->msg_len < DP_MAX_MSGLEN) {
dis->dreq_request.msg_type = request->msg_type;
dis->dreq_request.msg_len = request->msg_len;
dis->dreq_request.msg_buf =
dis->dreq_request_buf;
bcopy(request->msg_buf,
dis->dreq_request.msg_buf,
request->msg_len);
dis->dreq_state = RMC_COMM_DREQ_ST_PROCESS;
cv_signal(dis->dreq_sig_cv);
} else {
err = RCEREPTOOBIG;
}
} else {
DPRINTF(rcs, DAPI, (CE_CONT, "cannot get to send "
"request=%x (busy)\n", request->msg_type));
err = RCEGENERIC;
}
mutex_exit(dis->dreq_mutex);
}
return (err);
}
static int
rmc_comm_send_req_resp(struct rmc_comm_state *rcs, rmc_comm_msg_t *request,
rmc_comm_msg_t *response, uint32_t wait_time)
{
rmc_comm_dp_state_t *dps;
dp_req_resp_t *drr;
dp_message_t *exp_resp;
dp_message_t req;
clock_t resend_clockt, delta;
clock_t stop_clockt;
int err;
if (request == NULL) {
DPRINTF(rcs, DAPI, (CE_CONT, "reqresp, invalid args\n"));
return (RCEINVARG);
}
if (!IS_NUMBERED_MSG(request->msg_type)) {
DPRINTF(rcs, DAPI, (CE_CONT,
"reqresp, ctrl msg not allowed! req type=%x\n",
request->msg_type));
return (RCEINVARG);
}
dps = &rcs->dp_state;
drr = &dps->req_resp;
exp_resp = &drr->response;
if (ddi_in_panic() != 0) {
if (mutex_tryenter(dps->dp_mutex) == 0) {
return (RCENODATALINK);
}
} else {
mutex_enter(dps->dp_mutex);
}
if (!dps->data_link_ok &&
dps->timer_link_setup == (timeout_id_t)0) {
mutex_exit(dps->dp_mutex);
return (RCENODATALINK);
}
DPRINTF(rcs, DAPI, (CE_CONT, "pending request=%d, req type=%x\n",
dps->pending_request, request->msg_type));
rmc_comm_wait_enable_to_send(rcs, dps);
if (wait_time < DP_MIN_TIMEOUT)
wait_time = DP_MIN_TIMEOUT;
stop_clockt = ddi_get_lbolt() + drv_usectohz(wait_time * 1000);
drr->flags = 0;
drr->error_status = 0;
if (response != NULL) {
exp_resp->msg_type = response->msg_type;
exp_resp->msg_buf = (uint8_t *)response->msg_buf;
exp_resp->msg_msglen = response->msg_bytes;
exp_resp->msg_bufsiz = response->msg_len;
} else {
exp_resp->msg_type = DP_NULL_MSG;
exp_resp->msg_buf = NULL;
exp_resp->msg_bufsiz = 0;
exp_resp->msg_msglen = 0;
}
req.msg_type = request->msg_type;
req.msg_buf = (uint8_t *)request->msg_buf;
req.msg_msglen = request->msg_len;
DPRINTF(rcs, DAPI, (CE_CONT, "send request=%x\n", request->msg_type));
delta = drv_usectohz(TX_RETRY_TIME * 1000);
while ((err = rmc_comm_dp_msend(rcs, &req)) == RCNOERR) {
resend_clockt = ddi_get_lbolt() + delta;
(void) cv_reltimedwait(drr->cv_wait_reply, dps->dp_mutex,
delta, TR_CLOCK_TICK);
DPRINTF(rcs, DAPI, (CE_CONT,
"reqresp send status: flags=%02x req=%x resp=%x tick=%ld\n",
drr->flags, request->msg_type,
response ? response->msg_type : -1,
stop_clockt - resend_clockt));
if ((drr->flags & MSG_ERROR) != 0) {
if (drr->error_status == 0) {
err = RCEGENERIC;
} else {
err = drr->error_status;
}
break;
} else if (response != NULL &&
(drr->flags & MSG_REPLY_RXED) != 0) {
response->msg_bytes = exp_resp->msg_msglen;
if (response->msg_bytes < 0)
err = RCEREPTOOBIG;
else
err = RCNOERR;
break;
} else if (response == NULL && (drr->flags & MSG_ACKED) != 0) {
err = RCNOERR;
break;
} else if ((stop_clockt - resend_clockt) <= 0) {
err = RCETIMEOUT;
break;
}
}
rmc_comm_dp_mcleanup(rcs);
rmc_comm_wake_up_next(rcs);
mutex_exit(dps->dp_mutex);
DPRINTF(rcs, DAPI, (CE_CONT, "reqresp end: err=%d, request=%x\n",
err, request->msg_type));
return (err);
}
int
rmc_comm_request_response_bp(rmc_comm_msg_t *request_bp,
rmc_comm_msg_t *response_bp, uint32_t wait_time)
{
struct rmc_comm_state *rcs;
rmc_comm_dp_state_t *dps;
dp_req_resp_t *drr;
dp_message_t *resp_bp;
bp_msg_t *bp_msg;
clock_t stop_clockt;
int err = RCNOERR;
boolean_t bootinit_sent = 0;
if ((rcs = rmc_comm_getstate(NULL, 0,
"rmc_comm_request_response_bp")) == NULL)
return (RCENOSOFTSTATE);
if (request_bp == NULL) {
DPRINTF(rcs, DAPI, (CE_CONT, "reqresp_bp, invalid args\n"));
return (RCEINVARG);
}
bp_msg = (bp_msg_t *)request_bp->msg_buf;
DPRINTF(rcs, DAPI, (CE_CONT, "send request_bp=%x\n", bp_msg->cmd));
if (!IS_BOOT_MSG(bp_msg->cmd)) {
DPRINTF(rcs, DAPI, (CE_CONT,
"reqresp_bp, only BP msg are allowed! type=%x\n",
bp_msg->cmd));
return (RCEINVARG);
}
dps = &rcs->dp_state;
drr = &dps->req_resp;
resp_bp = &drr->response;
mutex_enter(dps->dp_mutex);
rmc_comm_wait_enable_to_send(rcs, dps);
if (bp_msg->cmd == BP_OBP_BOOTINIT) {
dps->data_link_ok = 0;
dps->timer_link_setup = (timeout_id_t)0;
bootinit_sent = 1;
} else if (bp_msg->cmd == BP_OBP_RESET) {
dp_reset(rcs, INITIAL_SEQID, 0, 1);
}
drr->flags = 0;
drr->error_status = 0;
if (response_bp != NULL) {
DPRINTF(rcs, DAPI, (CE_CONT, "expect BP reply. len=%d\n",
response_bp->msg_len));
resp_bp->msg_buf = (uint8_t *)response_bp->msg_buf;
resp_bp->msg_bufsiz = (uint16_t)response_bp->msg_len;
}
rmc_comm_bp_msend(rcs, bp_msg);
if (response_bp != NULL) {
if (wait_time < DP_MIN_TIMEOUT)
wait_time = DP_MIN_TIMEOUT;
stop_clockt = ddi_get_lbolt() + drv_usectohz(wait_time * 1000);
if ((err = rmc_comm_wait_bp_reply(rcs, dps, drr,
stop_clockt)) == RCNOERR) {
response_bp->msg_bytes = resp_bp->msg_msglen;
if (response_bp->msg_bytes < 0) {
err = RCEREPTOOBIG;
} else if (bootinit_sent) {
bp_msg = (bp_msg_t *)response_bp->msg_buf;
if (bp_msg->cmd == BP_RSC_BOOTFAIL &&
bp_msg->dat1 == BP_DAT1_REJECTED) {
(void) rmc_comm_dp_ctlsend(rcs,
DP_CTL_START);
}
}
}
}
rmc_comm_dp_mcleanup(rcs);
rmc_comm_wake_up_next(rcs);
mutex_exit(dps->dp_mutex);
return (err);
}
int
rmc_comm_reg_intr(uint8_t msg_type, rmc_comm_intrfunc_t intr_handler,
rmc_comm_msg_t *msgbuf, uint_t *state, kmutex_t *lock)
{
struct rmc_comm_state *rcs;
dp_msg_intr_t *msgintr;
int err = RCNOERR;
if ((rcs = rmc_comm_getstate(NULL, 0, "rmc_comm_reg_intr")) == NULL)
return (RCENOSOFTSTATE);
mutex_enter(rcs->dp_state.dp_mutex);
msgintr = &rcs->dp_state.msg_intr;
if (lock == NULL) {
mutex_exit(rcs->dp_state.dp_mutex);
return (RCEINVARG);
}
if (msgintr->intr_handler == NULL) {
if (ddi_add_softintr(rcs->dip, DDI_SOFTINT_HIGH,
&msgintr->intr_id, NULL, NULL, intr_handler,
(caddr_t)msgbuf) == DDI_SUCCESS) {
msgintr->intr_handler = intr_handler;
msgintr->intr_lock = lock;
msgintr->intr_state = state;
msgintr->intr_msg_type = msg_type;
msgintr->intr_arg = (caddr_t)msgbuf;
} else {
err = RCECANTREGINTR;
}
} else {
err = RCEALREADYREG;
}
mutex_exit(rcs->dp_state.dp_mutex);
return (err);
}
int
rmc_comm_unreg_intr(uint8_t msg_type, rmc_comm_intrfunc_t intr_handler)
{
struct rmc_comm_state *rcs;
dp_msg_intr_t *msgintr;
int err = RCNOERR;
if ((rcs = rmc_comm_getstate(NULL, 0, "rmc_comm_unreg_intr")) == NULL)
return (RCENOSOFTSTATE);
mutex_enter(rcs->dp_state.dp_mutex);
msgintr = &rcs->dp_state.msg_intr;
if (msgintr->intr_handler != NULL &&
msgintr->intr_msg_type == msg_type &&
msgintr->intr_handler == intr_handler) {
ddi_remove_softintr(msgintr->intr_id);
msgintr->intr_handler = NULL;
msgintr->intr_id = 0;
msgintr->intr_msg_type = 0;
msgintr->intr_arg = NULL;
msgintr->intr_lock = NULL;
msgintr->intr_state = NULL;
} else {
err = RCEGENERIC;
}
mutex_exit(rcs->dp_state.dp_mutex);
return (err);
}
int
rmc_comm_send_srecord_bp(caddr_t buf, int buflen,
rmc_comm_msg_t *response_bp, uint32_t wait_time)
{
struct rmc_comm_state *rcs;
rmc_comm_dp_state_t *dps;
dp_req_resp_t *drr;
dp_message_t *resp_bp;
clock_t stop_clockt;
int err;
if ((rcs = rmc_comm_getstate(NULL, 0,
"rmc_comm_request_response_bp")) == NULL)
return (RCENOSOFTSTATE);
if (buf == NULL || response_bp == NULL) {
DPRINTF(rcs, DAPI, (CE_CONT, "send_srecord_bp,invalid args\n"));
return (RCEINVARG);
}
DPRINTF(rcs, DAPI, (CE_CONT, "send_srecord_bp, buflen=%d\n", buflen));
dps = &rcs->dp_state;
drr = &dps->req_resp;
resp_bp = &drr->response;
mutex_enter(dps->dp_mutex);
rmc_comm_wait_enable_to_send(rcs, dps);
drr->flags = 0;
drr->error_status = 0;
resp_bp->msg_buf = (uint8_t *)response_bp->msg_buf;
resp_bp->msg_bufsiz = (uint16_t)response_bp->msg_len;
rmc_comm_bp_srecsend(rcs, (char *)buf, buflen);
if (wait_time < DP_MIN_TIMEOUT)
wait_time = DP_MIN_TIMEOUT;
stop_clockt = ddi_get_lbolt() + drv_usectohz(wait_time * 1000);
if ((err = rmc_comm_wait_bp_reply(rcs, dps, drr,
stop_clockt)) == RCNOERR) {
response_bp->msg_bytes = resp_bp->msg_msglen;
if (response_bp->msg_bytes < 0) {
err = RCEREPTOOBIG;
}
}
rmc_comm_dp_mcleanup(rcs);
rmc_comm_wake_up_next(rcs);
mutex_exit(dps->dp_mutex);
return (err);
}
static int
rmc_comm_wait_bp_reply(struct rmc_comm_state *rcs, rmc_comm_dp_state_t *dps,
dp_req_resp_t *drr, clock_t stop_clockt)
{
clock_t clockleft = 1;
int err = RCNOERR;
clockleft = cv_timedwait(drr->cv_wait_reply, dps->dp_mutex,
stop_clockt);
DPRINTF(rcs, DAPI, (CE_CONT,
"reqresp_bp, send: flags=%02x, clktick left=%ld\n",
drr->flags, clockleft));
if ((drr->flags & MSG_ERROR) != 0) {
err = RCEGENERIC;
} else if (clockleft <= 0) {
err = RCETIMEOUT;
} else if ((drr->flags & MSG_RXED_BP) == 0) {
err = RCEGENERIC;
}
return (err);
}
static void
rmc_comm_wait_enable_to_send(struct rmc_comm_state *rcs,
rmc_comm_dp_state_t *dps)
{
DPRINTF(rcs, DAPI, (CE_CONT, "pending request=%d\n",
dps->pending_request));
while (dps->pending_request) {
cv_wait(dps->cv_ok_to_send, dps->dp_mutex);
}
dps->pending_request = 1;
}
static void
rmc_comm_wake_up_next(struct rmc_comm_state *rcs)
{
rcs->dp_state.pending_request = 0;
cv_signal(rcs->dp_state.cv_ok_to_send);
}
static void
rmc_comm_send_pend_req(caddr_t arg)
{
struct rmc_comm_state *rcs;
rmc_comm_drvintf_state_t *dis;
callb_cpr_t cprinfo;
if (arg == NULL) {
thread_exit();
}
rcs = (struct rmc_comm_state *)arg;
dis = &rcs->drvi_state;
CALLB_CPR_INIT(&cprinfo, dis->dreq_mutex, callb_generic_cpr,
"rmc_comm_send_pend_req");
mutex_enter(dis->dreq_mutex);
if (dis->dreq_state <= RMC_COMM_DREQ_ST_READY)
dis->dreq_state = RMC_COMM_DREQ_ST_WAIT;
for (;;) {
while (dis->dreq_state == RMC_COMM_DREQ_ST_WAIT) {
CALLB_CPR_SAFE_BEGIN(&cprinfo);
cv_wait(dis->dreq_sig_cv, dis->dreq_mutex);
CALLB_CPR_SAFE_END(&cprinfo, dis->dreq_mutex);
}
if (dis->dreq_state == RMC_COMM_DREQ_ST_EXIT) {
dis->dreq_state = RMC_COMM_DREQ_ST_NOTSTARTED;
dis->dreq_tid = 0;
CALLB_CPR_EXIT(&cprinfo);
thread_exit();
}
ASSERT(dis->dreq_state == RMC_COMM_DREQ_ST_PROCESS);
mutex_exit(dis->dreq_mutex);
while (rmc_comm_send_req_resp(rcs, &dis->dreq_request, NULL,
RMC_COMM_DREQ_DEFAULT_TIME) == RCEGENERIC) {
}
mutex_enter(dis->dreq_mutex);
if (dis->dreq_state != RMC_COMM_DREQ_ST_EXIT)
dis->dreq_state = RMC_COMM_DREQ_ST_WAIT;
}
}
static int
rmc_comm_dreq_thread_start(struct rmc_comm_state *rcs)
{
rmc_comm_drvintf_state_t *dis = &rcs->drvi_state;
int err = 0;
kthread_t *tp;
mutex_enter(dis->dreq_mutex);
if (dis->dreq_state == RMC_COMM_DREQ_ST_NOTSTARTED) {
tp = thread_create(NULL, 0, rmc_comm_send_pend_req,
(caddr_t)rcs, 0, &p0, TS_RUN, maxclsyspri);
dis->dreq_state = RMC_COMM_DREQ_ST_READY;
dis->dreq_tid = tp->t_did;
}
mutex_exit(dis->dreq_mutex);
return (err);
}
static void
rmc_comm_dreq_thread_kill(struct rmc_comm_state *rcs)
{
rmc_comm_drvintf_state_t *dis = &rcs->drvi_state;
kt_did_t tid;
mutex_enter(dis->dreq_mutex);
tid = dis->dreq_tid;
if (tid != 0) {
dis->dreq_state = RMC_COMM_DREQ_ST_EXIT;
dis->dreq_tid = 0;
cv_signal(dis->dreq_sig_cv);
}
mutex_exit(dis->dreq_mutex);
if (tid != 0)
thread_join(tid);
}
int
rmc_comm_drvintf_init(struct rmc_comm_state *rcs)
{
int err = 0;
DPRINTF(rcs, DGEN, (CE_CONT, "rmc_comm_drvintf_init\n"));
rcs->drvi_state.dreq_state = RMC_COMM_DREQ_ST_NOTSTARTED;
rcs->drvi_state.dreq_tid = 0;
mutex_init(rcs->drvi_state.dreq_mutex, NULL, MUTEX_DRIVER, NULL);
cv_init(rcs->drvi_state.dreq_sig_cv, NULL, CV_DRIVER, NULL);
err = rmc_comm_dreq_thread_start(rcs);
if (err != 0) {
cv_destroy(rcs->drvi_state.dreq_sig_cv);
mutex_destroy(rcs->drvi_state.dreq_mutex);
}
DPRINTF(rcs, DGEN, (CE_CONT, "thread started? err=%d\n", err));
return (err);
}
void
rmc_comm_drvintf_fini(struct rmc_comm_state *rcs)
{
DPRINTF(rcs, DGEN, (CE_CONT, "rmc_comm_drvintf_fini:stop thread\n"));
rmc_comm_dreq_thread_kill(rcs);
DPRINTF(rcs, DGEN, (CE_CONT, "rmc_comm_drvintf_fini:destroy Mx/CVs\n"));
cv_destroy(rcs->drvi_state.dreq_sig_cv);
mutex_destroy(rcs->drvi_state.dreq_mutex);
}