1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27/*
28 * provide the interface to the layered drivers (send request/receive
29 * response to the RMC
30 *
31 */
32
33/*
34 *  Header files
35 */
36#include <sys/conf.h>
37#include <sys/callb.h>
38#include <sys/cyclic.h>
39#include <sys/membar.h>
40#include <sys/modctl.h>
41#include <sys/strlog.h>
42#include <sys/sunddi.h>
43#include <sys/ddi.h>
44#include <sys/types.h>
45#include <sys/disp.h>
46#include <sys/rmc_comm_dp.h>
47#include <sys/rmc_comm_dp_boot.h>
48#include <sys/rmc_comm_drvintf.h>
49#include <sys/rmc_comm.h>
50
51void dp_reset(struct rmc_comm_state *, uint8_t, boolean_t, boolean_t);
52void dp_wake_up_waiter(struct rmc_comm_state *, uint8_t);
53
54static int rmc_comm_send_req_resp(struct rmc_comm_state *rcs,
55    rmc_comm_msg_t *request, rmc_comm_msg_t *response, uint32_t wait_time);
56static int rmc_comm_wait_bp_reply(struct rmc_comm_state *,
57    rmc_comm_dp_state_t *, dp_req_resp_t *, clock_t);
58static void rmc_comm_wait_enable_to_send(struct rmc_comm_state *,
59    rmc_comm_dp_state_t *);
60static void rmc_comm_wake_up_next(struct rmc_comm_state *);
61static void rmc_comm_send_pend_req(caddr_t arg);
62static int rmc_comm_dreq_thread_start(struct rmc_comm_state *rcs);
63static void rmc_comm_dreq_thread_kill(struct rmc_comm_state *rcs);
64
65/*
66 * leaf driver to use this function to send a request to the remote side (RMC)
67 * and wait for a reply
68 */
69int
70rmc_comm_request_response(rmc_comm_msg_t *request,
71    rmc_comm_msg_t *response, uint32_t wait_time)
72{
73	struct rmc_comm_state	*rcs;
74	int err;
75
76	/*
77	 * get the soft state struct (instance 0)
78	 */
79	if ((rcs = rmc_comm_getstate(NULL, 0,
80	    "rmc_comm_request_response")) == NULL)
81		return (RCENOSOFTSTATE);
82
83	do {
84		err = rmc_comm_send_req_resp(rcs, request, response, wait_time);
85	} while (err == RCEGENERIC);
86	return (err);
87}
88
89/*
90 * leaf driver to use this function to send a request to the remote side (RMC)
91 * without waiting for a reply. If flag is RMC_COMM_DREQ_URGENT, the request
92 * message is sent once-off (an eventual pending request is aborted). This
93 * flag must only be used when try to send a request in critical condition
94 * (while the system is shutting down for instance and the CPU signature
95 * has to be sent). Otherwise, the request is stored in a temporary location
96 * and delivered by a thread.
97 */
98int
99rmc_comm_request_nowait(rmc_comm_msg_t *request, uint8_t flag)
100{
101	struct rmc_comm_state		*rcs;
102	rmc_comm_dp_state_t		*dps;
103	rmc_comm_drvintf_state_t	*dis;
104	dp_message_t			req;
105	int				err = RCNOERR;
106	uint8_t				flags = 0;
107
108	/*
109	 * get the soft state struct (instance 0)
110	 */
111	if ((rcs = rmc_comm_getstate(NULL, 0,
112	    "rmc_comm_request_response")) == NULL)
113		return (RCENOSOFTSTATE);
114
115	/*
116	 * just a sanity check...
117	 */
118	if (request == NULL) {
119		DPRINTF(rcs, DAPI, (CE_CONT, "reqnowait, invalid args\n"));
120		return (RCEINVARG);
121	}
122
123	if (!IS_NUMBERED_MSG(request->msg_type)) {
124		DPRINTF(rcs, DAPI, (CE_CONT,
125		    "reqnowait, ctrl msg not allowed! req type=%x\n",
126		    request->msg_type));
127		return (RCEINVARG);
128	}
129
130	if (flag == RMC_COMM_DREQ_URGENT) {
131		/*
132		 * Send this request with high priority i.e. abort eventual
133		 * request/response pending sessions.
134		 */
135
136		dps = &rcs->dp_state;
137
138		DPRINTF(rcs, DAPI, (CE_CONT, "going to send request=%x (URG)\n",
139		    request->msg_type));
140
141		/*
142		 * Handle the case where we are called during panic
143		 * processing.  If that occurs, then another thread in
144		 * rmc_comm might have been idled by panic() while
145		 * holding dp_mutex.  As a result, do not unconditionally
146		 * grab dp_mutex.
147		 */
148		if (ddi_in_panic() != 0) {
149			if (mutex_tryenter(dps->dp_mutex) == 0) {
150				return (RCENODATALINK);
151			}
152		} else {
153			mutex_enter(dps->dp_mutex);
154		}
155
156		/*
157		 * send the request only if the protocol data link is up.
158		 * it is pointless to send it in the other case.
159		 */
160		if (dps->data_link_ok) {
161
162			/*
163			 * clean up an eventual pending request/response session
164			 * (save its current status)
165			 */
166			if (dps->pending_request) {
167				flags = dps->req_resp.flags;
168				rmc_comm_dp_mcleanup(rcs);
169			}
170
171			/*
172			 * send the request message
173			 */
174			req.msg_type = request->msg_type;
175			req.msg_buf = (uint8_t *)request->msg_buf;
176			req.msg_msglen = (uint16_t)request->msg_len;
177
178			DPRINTF(rcs, DAPI, (CE_CONT, "send request=%x (URG)\n",
179			    request->msg_type));
180
181			err = rmc_comm_dp_msend(rcs, &req);
182
183			/*
184			 * wait for fifos to drain
185			 */
186			rmc_comm_serdev_drain(rcs);
187
188			/*
189			 * clean up the current session
190			 */
191			rmc_comm_dp_mcleanup(rcs);
192
193			/*
194			 * abort an old session (if any)
195			 */
196			if (dps->pending_request) {
197				dps->req_resp.flags = flags;
198				dp_wake_up_waiter(rcs, MSG_ERROR);
199			}
200		}
201
202		mutex_exit(dps->dp_mutex);
203
204	} else {
205
206		/*
207		 * Get an 'independent' thread (rmc_comm_send_pend_req)
208		 * to send this request (since the calling thread does not
209		 * want to wait). Copy the request in the drvintf state
210		 * structure and signal the thread.
211		 */
212
213		dis = &rcs->drvi_state;
214
215		mutex_enter(dis->dreq_mutex);
216
217		if (dis->dreq_state == RMC_COMM_DREQ_ST_WAIT) {
218
219			DPRINTF(rcs, DAPI, (CE_CONT, "get to send request=%x\n",
220			    request->msg_type));
221
222			/*
223			 * copy the request in a temporary location
224			 * (drvinf_state structure) and signal the thread
225			 * that a request message has to be delivered
226			 */
227
228			if (request->msg_len < DP_MAX_MSGLEN) {
229				dis->dreq_request.msg_type = request->msg_type;
230				dis->dreq_request.msg_len = request->msg_len;
231				dis->dreq_request.msg_buf =
232				    dis->dreq_request_buf;
233				bcopy(request->msg_buf,
234				    dis->dreq_request.msg_buf,
235				    request->msg_len);
236
237				dis->dreq_state = RMC_COMM_DREQ_ST_PROCESS;
238				cv_signal(dis->dreq_sig_cv);
239
240			} else {
241				/*
242				 * not enough space to hold the request
243				 */
244				err = RCEREPTOOBIG;
245			}
246		} else {
247
248			DPRINTF(rcs, DAPI, (CE_CONT, "cannot get to send "
249			    "request=%x (busy)\n", request->msg_type));
250
251			/*
252			 * only one request per time can be processed.
253			 * the thread is either busy (RMC_COMM_DREQ_ST_PROCESS)
254			 * or terminating (RMC_COMM_DREQ_ST_EXIT)
255			 */
256			err = RCEGENERIC;
257		}
258
259		mutex_exit(dis->dreq_mutex);
260	}
261
262	return (err);
263}
264
265/*
266 * Function used to send a request and (eventually) wait for a response.
267 * It can be called from a leaf driver (via rmc_comm_request_response) or
268 * from the thread in charge of sending 'no-wait' requests
269 * (rmc_comm_send_pend_req).
270 */
271static int
272rmc_comm_send_req_resp(struct rmc_comm_state *rcs, rmc_comm_msg_t *request,
273    rmc_comm_msg_t *response, uint32_t wait_time)
274{
275	rmc_comm_dp_state_t	*dps;
276	dp_req_resp_t		*drr;
277	dp_message_t		*exp_resp;
278	dp_message_t		req;
279	clock_t			resend_clockt, delta;
280	clock_t			stop_clockt;
281	int			err;
282
283
284	/*
285	 * just a sanity check...
286	 */
287	if (request == NULL) {
288		DPRINTF(rcs, DAPI, (CE_CONT, "reqresp, invalid args\n"));
289		return (RCEINVARG);
290	}
291
292	/*
293	 * drivers cannot send control messages at all. They are meant to
294	 * be used at low level only.
295	 */
296	if (!IS_NUMBERED_MSG(request->msg_type)) {
297		DPRINTF(rcs, DAPI, (CE_CONT,
298		    "reqresp, ctrl msg not allowed! req type=%x\n",
299		    request->msg_type));
300		return (RCEINVARG);
301	}
302
303	dps = &rcs->dp_state;
304	drr = &dps->req_resp;
305	exp_resp = &drr->response;
306
307	/*
308	 * Handle the case where we are called during panic
309	 * processing.  If that occurs, then another thread in
310	 * rmc_comm might have been idled by panic() while
311	 * holding dp_mutex.  As a result, do not unconditionally
312	 * grab dp_mutex.
313	 */
314	if (ddi_in_panic() != 0) {
315		if (mutex_tryenter(dps->dp_mutex) == 0) {
316			return (RCENODATALINK);
317		}
318	} else {
319		mutex_enter(dps->dp_mutex);
320	}
321
322	/*
323	 * if the data link set up is suspended, just return.
324	 * the only time that this can happen is during firmware download
325	 * (see rmc_comm_request_response_bp). Basically, the data link is
326	 * down and the timer for setting up the data link is not running.
327	 */
328	if (!dps->data_link_ok &&
329	    dps->timer_link_setup == (timeout_id_t)0) {
330
331		mutex_exit(dps->dp_mutex);
332		return (RCENODATALINK);
333	}
334
335	DPRINTF(rcs, DAPI, (CE_CONT, "pending request=%d, req type=%x\n",
336	    dps->pending_request, request->msg_type));
337
338	rmc_comm_wait_enable_to_send(rcs, dps);
339
340	/*
341	 * We now have control of the RMC.
342	 * Place a lower limit on the shortest amount of time to be
343	 * waited before timing out while communicating with the RMC
344	 */
345	if (wait_time < DP_MIN_TIMEOUT)
346		wait_time = DP_MIN_TIMEOUT;
347
348	stop_clockt = ddi_get_lbolt() + drv_usectohz(wait_time * 1000);
349
350	/*
351	 * initialization of the request/response data structure
352	 */
353	drr->flags = 0;
354	drr->error_status = 0;
355
356	/*
357	 * set the 'expected reply' buffer: get the buffer already allocated
358	 * for the response (if a reply is expected!)
359	 */
360	if (response != NULL) {
361		exp_resp->msg_type = response->msg_type;
362		exp_resp->msg_buf = (uint8_t *)response->msg_buf;
363		exp_resp->msg_msglen = (uint16_t)response->msg_bytes;
364		exp_resp->msg_bufsiz = (uint16_t)response->msg_len;
365	} else {
366		exp_resp->msg_type = DP_NULL_MSG;
367		exp_resp->msg_buf = (uint8_t)NULL;
368		exp_resp->msg_bufsiz = (uint16_t)0;
369		exp_resp->msg_msglen = (uint16_t)0;
370	}
371
372	/*
373	 * send the request message
374	 */
375	req.msg_type = request->msg_type;
376	req.msg_buf = (uint8_t *)request->msg_buf;
377	req.msg_msglen = (uint16_t)request->msg_len;
378
379	/*
380	 * send the message and wait for the reply or ACKnowledgment
381	 * re-send the message if reply/ACK is not received in the
382	 * timeframe defined
383	 */
384	DPRINTF(rcs, DAPI, (CE_CONT, "send request=%x\n", request->msg_type));
385
386	delta = drv_usectohz(TX_RETRY_TIME * 1000);
387
388	while ((err = rmc_comm_dp_msend(rcs, &req)) == RCNOERR) {
389
390		resend_clockt = ddi_get_lbolt() + delta;
391
392		/*
393		 * wait for a reply or an acknowledgement
394		 */
395		(void) cv_reltimedwait(drr->cv_wait_reply, dps->dp_mutex,
396		    delta, TR_CLOCK_TICK);
397
398		DPRINTF(rcs, DAPI, (CE_CONT,
399		    "reqresp send status: flags=%02x req=%x resp=%x tick=%ld\n",
400		    drr->flags, request->msg_type,
401		    response ? response->msg_type : -1,
402		    stop_clockt - resend_clockt));
403
404		/*
405		 * Check for error condition first
406		 * Then, check if the command has been replied/ACKed
407		 * Then, check if it has timeout and if there is any
408		 * time left to resend the message.
409		 */
410		if ((drr->flags & MSG_ERROR) != 0) {
411			if (drr->error_status == 0) {
412				err = RCEGENERIC;
413			} else {
414				err = drr->error_status;
415			}
416			break;
417
418		} else if (response != NULL &&
419		    (drr->flags & MSG_REPLY_RXED) != 0) {
420			/*
421			 * yes! here is the reply
422			 */
423
424			/*
425			 * get the actual length of the msg
426			 * a negative value means that the reply message
427			 * was too big for the receiver buffer
428			 */
429			response->msg_bytes = exp_resp->msg_msglen;
430			if (response->msg_bytes < 0)
431				err = RCEREPTOOBIG;
432			else
433				err = RCNOERR;
434			break;
435
436		} else if (response == NULL && (drr->flags & MSG_ACKED) != 0) {
437			/*
438			 * yes! message has been acknowledged
439			 */
440
441			err = RCNOERR;
442			break;
443
444		} else if ((stop_clockt - resend_clockt) <= 0) {
445			/*
446			 * no more time left. set the error code,
447			 * exit the loop
448			 */
449
450			err = RCETIMEOUT;
451			break;
452		}
453	}
454
455	rmc_comm_dp_mcleanup(rcs);
456
457	rmc_comm_wake_up_next(rcs);
458
459	mutex_exit(dps->dp_mutex);
460
461	DPRINTF(rcs, DAPI, (CE_CONT, "reqresp end: err=%d, request=%x\n",
462	    err, request->msg_type));
463
464	return (err);
465}
466
467/*
468 * Function used to send a BP (Boot Prom) message and get the reply.
469 * BP protocol is provided only to support firmware download.
470 *
471 * This function will look for the following key BP protocol commands:
472 * BP_OBP_BOOTINIT: the data link is brought down so that request/response
473 * sessions cannot be started. The reason why is that this command will cause
474 * RMC fw to jump to the boot monitor (BOOTMON_FLASH) and data protocol is not
475 * operational. In this context, RMC fw will only be using the BP protocol.
476 * BP_OBP_RESET: data link setup timer is resumed. This command cause the RMC
477 * to reboot and hence become operational.
478 */
479int
480rmc_comm_request_response_bp(rmc_comm_msg_t *request_bp,
481    rmc_comm_msg_t *response_bp, uint32_t wait_time)
482{
483	struct rmc_comm_state	*rcs;
484	rmc_comm_dp_state_t	*dps;
485	dp_req_resp_t		*drr;
486	dp_message_t		*resp_bp;
487	bp_msg_t		*bp_msg;
488	clock_t			stop_clockt;
489	int			err = RCNOERR;
490	boolean_t		bootinit_sent = 0;
491
492	/*
493	 * get the soft state struct (instance 0)
494	 */
495	if ((rcs = rmc_comm_getstate(NULL, 0,
496	    "rmc_comm_request_response_bp")) == NULL)
497		return (RCENOSOFTSTATE);
498
499	/*
500	 * sanity check: request_bp buffer must always be provided
501	 */
502	if (request_bp == NULL) {
503		DPRINTF(rcs, DAPI, (CE_CONT, "reqresp_bp, invalid args\n"));
504		return (RCEINVARG);
505	}
506
507	bp_msg = (bp_msg_t *)request_bp->msg_buf;
508
509	DPRINTF(rcs, DAPI, (CE_CONT, "send request_bp=%x\n", bp_msg->cmd));
510
511	/*
512	 * only BP message can be sent
513	 */
514	if (!IS_BOOT_MSG(bp_msg->cmd)) {
515		DPRINTF(rcs, DAPI, (CE_CONT,
516		    "reqresp_bp, only BP msg are allowed! type=%x\n",
517		    bp_msg->cmd));
518		return (RCEINVARG);
519	}
520
521	dps = &rcs->dp_state;
522	drr = &dps->req_resp;
523	resp_bp = &drr->response;
524
525	mutex_enter(dps->dp_mutex);
526
527	rmc_comm_wait_enable_to_send(rcs, dps);
528
529	/*
530	 * Now, before sending the message, just check what it is being sent
531	 * and take action accordingly.
532	 *
533	 * is it BP_OBP_BOOTINIT or BP_OBP_RESET command?
534	 */
535	if (bp_msg->cmd == BP_OBP_BOOTINIT) {
536
537		/*
538		 * bring down the protocol data link
539		 * (must be done before aborting a request/response session)
540		 */
541		dps->data_link_ok = 0;
542		dps->timer_link_setup = (timeout_id_t)0;
543
544		bootinit_sent = 1;
545
546	} else if (bp_msg->cmd == BP_OBP_RESET) {
547
548		/*
549		 * restart the data link set up timer. RMC is coming up...
550		 */
551
552		dp_reset(rcs, INITIAL_SEQID, 0, 1);
553	}
554
555	/*
556	 * initialization of the request/response data structure
557	 */
558	drr->flags = 0;
559	drr->error_status = 0;
560
561	/*
562	 * set the reply buffer: get the buffer already allocated
563	 * for the response
564	 */
565	if (response_bp != NULL) {
566		DPRINTF(rcs, DAPI, (CE_CONT, "expect BP reply. len=%d\n",
567		    response_bp->msg_len));
568
569		resp_bp->msg_buf = (uint8_t *)response_bp->msg_buf;
570		resp_bp->msg_bufsiz = (uint16_t)response_bp->msg_len;
571	}
572
573	/*
574	 * send the BP message and wait for the reply
575	 */
576
577	rmc_comm_bp_msend(rcs, bp_msg);
578
579	if (response_bp != NULL) {
580
581		/*
582		 * place a lower limit on the shortest amount of time to be
583		 * waited before timing out while communicating with the RMC
584		 */
585		if (wait_time < DP_MIN_TIMEOUT)
586			wait_time = DP_MIN_TIMEOUT;
587
588		stop_clockt = ddi_get_lbolt() + drv_usectohz(wait_time * 1000);
589
590		if ((err = rmc_comm_wait_bp_reply(rcs, dps, drr,
591		    stop_clockt)) == RCNOERR) {
592
593			/*
594			 * get the actual length of the msg
595			 * a negative value means that the reply message
596			 * was too big for the receiver buffer
597			 */
598			response_bp->msg_bytes = resp_bp->msg_msglen;
599			if (response_bp->msg_bytes < 0) {
600				err = RCEREPTOOBIG;
601
602			} else if (bootinit_sent) {
603
604				/*
605				 * BOOTINIT cmd may fail. In this is the case,
606				 * the RMC is still operational. Hence, we
607				 * try (once) to set up the data link
608				 * protocol.
609				 */
610				bp_msg = (bp_msg_t *)response_bp->msg_buf;
611
612				if (bp_msg->cmd == BP_RSC_BOOTFAIL &&
613				    bp_msg->dat1 == BP_DAT1_REJECTED) {
614					(void) rmc_comm_dp_ctlsend(rcs,
615					    DP_CTL_START);
616				}
617			}
618		}
619	}
620
621	rmc_comm_dp_mcleanup(rcs);
622
623	rmc_comm_wake_up_next(rcs);
624
625	mutex_exit(dps->dp_mutex);
626
627	return (err);
628}
629
630
631/*
632 * to register for an asynchronous (via soft interrupt) notification
633 * of a message from the remote side (RMC)
634 */
635int
636rmc_comm_reg_intr(uint8_t msg_type, rmc_comm_intrfunc_t intr_handler,
637    rmc_comm_msg_t *msgbuf, uint_t *state, kmutex_t *lock)
638{
639	struct rmc_comm_state 	*rcs;
640	dp_msg_intr_t		*msgintr;
641	int			 err = RCNOERR;
642
643	if ((rcs = rmc_comm_getstate(NULL, 0, "rmc_comm_reg_intr")) == NULL)
644		return (RCENOSOFTSTATE);
645
646	mutex_enter(rcs->dp_state.dp_mutex);
647
648	msgintr = &rcs->dp_state.msg_intr;
649
650	/*
651	 * lock is required. If it is not defined, the
652	 * interrupt handler routine cannot be registered.
653	 */
654	if (lock == NULL) {
655		mutex_exit(rcs->dp_state.dp_mutex);
656		return (RCEINVARG);
657	}
658
659	/*
660	 * only one interrupt handler can be registered.
661	 */
662	if (msgintr->intr_handler == NULL) {
663
664		if (ddi_add_softintr(rcs->dip, DDI_SOFTINT_HIGH,
665		    &msgintr->intr_id, NULL, NULL, intr_handler,
666		    (caddr_t)msgbuf) == DDI_SUCCESS) {
667
668			msgintr->intr_handler = intr_handler;
669			msgintr->intr_lock = lock;
670			msgintr->intr_state = state;
671			msgintr->intr_msg_type = msg_type;
672			msgintr->intr_arg = (caddr_t)msgbuf;
673		} else {
674			err = RCECANTREGINTR;
675		}
676	} else {
677		err = RCEALREADYREG;
678	}
679
680	mutex_exit(rcs->dp_state.dp_mutex);
681
682	return (err);
683}
684
685/*
686 * To unregister for asynchronous notifications
687 */
688int
689rmc_comm_unreg_intr(uint8_t msg_type, rmc_comm_intrfunc_t intr_handler)
690{
691	struct rmc_comm_state	*rcs;
692	dp_msg_intr_t		*msgintr;
693	int			 err = RCNOERR;
694
695	if ((rcs = rmc_comm_getstate(NULL, 0, "rmc_comm_unreg_intr")) == NULL)
696		return (RCENOSOFTSTATE);
697
698	mutex_enter(rcs->dp_state.dp_mutex);
699
700	msgintr = &rcs->dp_state.msg_intr;
701
702	if (msgintr->intr_handler != NULL &&
703	    msgintr->intr_msg_type == msg_type &&
704	    msgintr->intr_handler == intr_handler) {
705
706		ddi_remove_softintr(msgintr->intr_id);
707		msgintr->intr_handler = NULL;
708		msgintr->intr_id = 0;
709		msgintr->intr_msg_type = 0;
710		msgintr->intr_arg = NULL;
711		msgintr->intr_lock = NULL;
712		msgintr->intr_state = NULL;
713	} else {
714		err = RCEGENERIC;
715	}
716
717	mutex_exit(rcs->dp_state.dp_mutex);
718
719	return (err);
720}
721
722/*
723 * To send raw data (firmware s-records) down to the RMC.
724 * It is provided only to support firmware download.
725 */
726int
727rmc_comm_send_srecord_bp(caddr_t buf, int buflen,
728    rmc_comm_msg_t *response_bp, uint32_t wait_time)
729{
730	struct rmc_comm_state	*rcs;
731	rmc_comm_dp_state_t	*dps;
732	dp_req_resp_t		*drr;
733	dp_message_t		*resp_bp;
734	clock_t			stop_clockt;
735	int			err;
736
737	/*
738	 * get the soft state struct (instance 0)
739	 */
740	if ((rcs = rmc_comm_getstate(NULL, 0,
741	    "rmc_comm_request_response_bp")) == NULL)
742		return (RCENOSOFTSTATE);
743
744	/*
745	 * sanity check: response_bp buffer must always be provided
746	 */
747	if (buf == NULL || response_bp == NULL) {
748		DPRINTF(rcs, DAPI, (CE_CONT, "send_srecord_bp,invalid args\n"));
749		return (RCEINVARG);
750	}
751
752	DPRINTF(rcs, DAPI, (CE_CONT, "send_srecord_bp, buflen=%d\n", buflen));
753
754	dps = &rcs->dp_state;
755	drr = &dps->req_resp;
756	resp_bp = &drr->response;
757
758	mutex_enter(dps->dp_mutex);
759
760	rmc_comm_wait_enable_to_send(rcs, dps);
761
762	/*
763	 * initialization of the request/response data structure
764	 */
765	drr->flags = 0;
766	drr->error_status = 0;
767
768	/*
769	 * set the reply buffer: get the buffer already allocated
770	 * for the response
771	 */
772	resp_bp->msg_buf = (uint8_t *)response_bp->msg_buf;
773	resp_bp->msg_bufsiz = (uint16_t)response_bp->msg_len;
774
775	/*
776	 * send raw data (s-record) and wait for the reply (BP message)
777	 */
778
779	rmc_comm_bp_srecsend(rcs, (char *)buf, buflen);
780
781	/*
782	 * place a lower limit on the shortest amount of time to be
783	 * waited before timing out while communicating with the RMC
784	 */
785	if (wait_time < DP_MIN_TIMEOUT)
786		wait_time = DP_MIN_TIMEOUT;
787
788	stop_clockt = ddi_get_lbolt() + drv_usectohz(wait_time * 1000);
789
790	if ((err = rmc_comm_wait_bp_reply(rcs, dps, drr,
791	    stop_clockt)) == RCNOERR) {
792		/*
793		 * get the actual length of the msg
794		 * a negative value means that the reply message
795		 * was too big for the receiver buffer
796		 */
797		response_bp->msg_bytes = resp_bp->msg_msglen;
798		if (response_bp->msg_bytes < 0) {
799			err = RCEREPTOOBIG;
800		}
801	}
802
803	rmc_comm_dp_mcleanup(rcs);
804
805	rmc_comm_wake_up_next(rcs);
806
807	mutex_exit(dps->dp_mutex);
808
809	return (err);
810}
811
812/*
813 * To wait for (any) BP message to be received.
814 * (dp_mutex must be held)
815 */
816static int
817rmc_comm_wait_bp_reply(struct rmc_comm_state *rcs, rmc_comm_dp_state_t *dps,
818    dp_req_resp_t *drr, clock_t stop_clockt)
819{
820	clock_t clockleft = 1;
821	int err = RCNOERR;
822
823	clockleft = cv_timedwait(drr->cv_wait_reply, dps->dp_mutex,
824	    stop_clockt);
825
826
827	DPRINTF(rcs, DAPI, (CE_CONT,
828	    "reqresp_bp, send: flags=%02x, clktick left=%ld\n",
829	    drr->flags, clockleft));
830
831	/*
832	 * Check for error condition first.
833	 * Then, check if it has timeout.
834	 * Then, check if the command has been replied.
835	 */
836	if ((drr->flags & MSG_ERROR) != 0) {
837
838		err = RCEGENERIC;
839
840	} else if (clockleft <= 0) {
841		/*
842		 * timeout
843		 */
844
845		err = RCETIMEOUT;
846
847	} else if ((drr->flags & MSG_RXED_BP) == 0) {
848
849		err = RCEGENERIC;
850	}
851
852	return (err);
853}
854
855/*
856 * Wait for the pending_request flag to be cleared and acquire it for our
857 * own use. The caller is then allowed to start a new request/response
858 * session with the RMC.
859 * Note that all send-receive actions to the RMC include a time-out, so
860 * the pending-request must eventually go away - even if the RMC is down.
861 * Hence there is no need to timeout the wait action of this function.
862 * (dp_mutex must be held on entry).
863 */
864static void
865rmc_comm_wait_enable_to_send(struct rmc_comm_state *rcs,
866    rmc_comm_dp_state_t *dps)
867{
868	DPRINTF(rcs, DAPI, (CE_CONT, "pending request=%d\n",
869	    dps->pending_request));
870
871	/*
872	 * A new message can actually grab the lock before the thread
873	 * that has just been signaled.  Therefore, we need to double
874	 * check to make sure that pending_request is not already set
875	 * after we wake up.
876	 *
877	 * Potentially this could mean starvation for certain unfortunate
878	 * threads that keep getting woken up and putting back to sleep.
879	 * But the window of such contention is very small to begin with.
880	 */
881
882	while (dps->pending_request) {
883		/*
884		 * just 'sit and wait' until there are no pending requests
885		 */
886
887		cv_wait(dps->cv_ok_to_send, dps->dp_mutex);
888	}
889
890	/*
891	 * now a request/response can be started. Set the flag so that nobody
892	 * else will be able to send anything.
893	 */
894	dps->pending_request = 1;
895}
896
897/*
898 * To wake up one of the threads (if any) waiting for starting a
899 * request/response session.
900 * (dp_mutex must be held)
901 */
902static void
903rmc_comm_wake_up_next(struct rmc_comm_state *rcs)
904{
905	/*
906	 * wake up eventual waiting threads...
907	 */
908
909	rcs->dp_state.pending_request = 0;
910	cv_signal(rcs->dp_state.cv_ok_to_send);
911}
912
913
914/*
915 * thread which delivers pending request message to the rmc. Some leaf drivers
916 * cannot afford to wait for a request to be replied/ACKed. Hence, a request
917 * message is stored temporarily in the state structure and this thread
918 * gets woken up to deliver it.
919 */
920static void
921rmc_comm_send_pend_req(caddr_t arg)
922{
923	struct rmc_comm_state		*rcs;
924	rmc_comm_drvintf_state_t	*dis;
925	callb_cpr_t			cprinfo;
926
927	if (arg == NULL) {
928		thread_exit();
929		/* NOTREACHED */
930	}
931
932	rcs = (struct rmc_comm_state *)arg;
933	dis = &rcs->drvi_state;
934
935	CALLB_CPR_INIT(&cprinfo, dis->dreq_mutex, callb_generic_cpr,
936	    "rmc_comm_send_pend_req");
937
938	mutex_enter(dis->dreq_mutex);
939
940	if (dis->dreq_state <= RMC_COMM_DREQ_ST_READY)
941		dis->dreq_state = RMC_COMM_DREQ_ST_WAIT;
942
943	for (;;) {
944
945		/*
946		 * Wait for someone to tell me to continue.
947		 */
948		while (dis->dreq_state == RMC_COMM_DREQ_ST_WAIT) {
949			CALLB_CPR_SAFE_BEGIN(&cprinfo);
950			cv_wait(dis->dreq_sig_cv, dis->dreq_mutex);
951			CALLB_CPR_SAFE_END(&cprinfo, dis->dreq_mutex);
952		}
953
954		/* RMC_COMM_DREQ_ST_EXIT implies signal by _detach(). */
955		if (dis->dreq_state == RMC_COMM_DREQ_ST_EXIT) {
956			dis->dreq_state = RMC_COMM_DREQ_ST_NOTSTARTED;
957			dis->dreq_tid = 0;
958
959			/* dis->dreq_mutex is held at this point! */
960			CALLB_CPR_EXIT(&cprinfo);
961
962			thread_exit();
963			/* NOTREACHED */
964		}
965
966		ASSERT(dis->dreq_state == RMC_COMM_DREQ_ST_PROCESS);
967		mutex_exit(dis->dreq_mutex);
968
969		/*
970		 * deliver the request (and wait...)
971		 */
972		while (rmc_comm_send_req_resp(rcs, &dis->dreq_request, NULL,
973		    RMC_COMM_DREQ_DEFAULT_TIME) == RCEGENERIC) {
974		}
975
976		mutex_enter(dis->dreq_mutex);
977		if (dis->dreq_state != RMC_COMM_DREQ_ST_EXIT)
978			dis->dreq_state = RMC_COMM_DREQ_ST_WAIT;
979	}
980}
981
982/*
983 * start thread to deal with pending requests to be delivered asynchronously
984 * (i.e. leaf driver do not have to/cannot wait for a reply/ACk of a request)
985 */
986static int
987rmc_comm_dreq_thread_start(struct rmc_comm_state *rcs)
988{
989	rmc_comm_drvintf_state_t *dis = &rcs->drvi_state;
990	int err = 0;
991	kthread_t *tp;
992
993	mutex_enter(dis->dreq_mutex);
994
995	if (dis->dreq_state == RMC_COMM_DREQ_ST_NOTSTARTED) {
996
997		tp = thread_create(NULL, 0, rmc_comm_send_pend_req,
998		    (caddr_t)rcs, 0, &p0, TS_RUN, maxclsyspri);
999		dis->dreq_state = RMC_COMM_DREQ_ST_READY;
1000		dis->dreq_tid = tp->t_did;
1001	}
1002
1003	mutex_exit(dis->dreq_mutex);
1004
1005	return (err);
1006}
1007
1008/*
1009 * stop the thread (to deliver pending request messages)
1010 */
1011static void
1012rmc_comm_dreq_thread_kill(struct rmc_comm_state *rcs)
1013{
1014	rmc_comm_drvintf_state_t *dis = &rcs->drvi_state;
1015	kt_did_t tid;
1016
1017	mutex_enter(dis->dreq_mutex);
1018	tid = dis->dreq_tid;
1019	if (tid != 0) {
1020		dis->dreq_state = RMC_COMM_DREQ_ST_EXIT;
1021		dis->dreq_tid = 0;
1022		cv_signal(dis->dreq_sig_cv);
1023	}
1024	mutex_exit(dis->dreq_mutex);
1025
1026	/*
1027	 * Wait for rmc_comm_send_pend_req() to finish
1028	 */
1029	if (tid != 0)
1030		thread_join(tid);
1031}
1032
1033/*
1034 * init function - start thread to deal with pending requests (no-wait requests)
1035 */
1036int
1037rmc_comm_drvintf_init(struct rmc_comm_state *rcs)
1038{
1039	int err = 0;
1040
1041	DPRINTF(rcs, DGEN, (CE_CONT, "rmc_comm_drvintf_init\n"));
1042	rcs->drvi_state.dreq_state = RMC_COMM_DREQ_ST_NOTSTARTED;
1043	rcs->drvi_state.dreq_tid = 0;
1044
1045	mutex_init(rcs->drvi_state.dreq_mutex, NULL, MUTEX_DRIVER, NULL);
1046	cv_init(rcs->drvi_state.dreq_sig_cv, NULL, CV_DRIVER, NULL);
1047
1048	err = rmc_comm_dreq_thread_start(rcs);
1049	if (err != 0) {
1050		cv_destroy(rcs->drvi_state.dreq_sig_cv);
1051		mutex_destroy(rcs->drvi_state.dreq_mutex);
1052	}
1053
1054	DPRINTF(rcs, DGEN, (CE_CONT, "thread started? err=%d\n", err));
1055
1056	return (err);
1057}
1058
1059/*
1060 * fini function - kill thread to deal with pending requests (no-wait requests)
1061 */
1062void
1063rmc_comm_drvintf_fini(struct rmc_comm_state *rcs)
1064{
1065	DPRINTF(rcs, DGEN, (CE_CONT, "rmc_comm_drvintf_fini:stop thread\n"));
1066
1067	rmc_comm_dreq_thread_kill(rcs);
1068
1069	DPRINTF(rcs, DGEN, (CE_CONT, "rmc_comm_drvintf_fini:destroy Mx/CVs\n"));
1070
1071	cv_destroy(rcs->drvi_state.dreq_sig_cv);
1072	mutex_destroy(rcs->drvi_state.dreq_mutex);
1073}
1074