/*
* 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.
*/
/*
* DM2S - Domain side Mailbox to synchronous serial device driver.
*
* Description:
* -----------
* It is a streams driver which simulates a sync serial device on
* frames as mailbox messages. The mailbox communication is provided
* by another driver, which exports the mailbox interfaces.
*
* Synchronization:
* ---------------
* This driver uses streams perimeters to simplify the synchronization.
* An inner perimeter D_MTPERMOD which protects the entire module,
* that is only one thread exists inside the perimeter, is used. As
* this driver supports only one instance and is not a high-performance
* driver, D_MTPERMOD is highly suitable.
*
* All transmission and reception of frames is done inside the service
* procedures so that all streams related operations are protected
* by the perimeters.
*
* The mailbox event handler is the only asynchronous callback which
* needs to be protected outside of the streams perimeters. This is
* done using the module private lock('ms_lock');
*
*/
#include <sys/ser_sync.h>
#include <sys/sysmacros.h>
/*
* Global variables
*/
/*
* Prototypes for the module related functions.
*/
/*
* Prototypes for the streams related functions.
*/
/*
* Prototypes for the internal functions.
*/
void dm2s_wq_timeout(void *arg);
void dm2s_rq_timeout(void *arg);
void dm2s_bufcall_rcv(void *arg);
#ifdef DEBUG
#endif /* DEBUG */
/*
* Streams and module related structures.
*/
DM2S_ID_NUM, /* module ID number */
DM2S_MODNAME, /* module name. */
0, /* Minimum packet size (none) */
DM2S_MAXPSZ, /* Maximum packet size (none) */
DM2S_HIWAT, /* queue high water mark */
DM2S_LOWAT /* queue low water mark */
};
putq, /* qi_putp */
dm2s_rsrv, /* qi_srvp */
dm2s_open, /* qi_qopen */
dm2s_close, /* qi_qlcose */
NULL, /* qi_qadmin */
&dm2s_module_info, /* qi_minfo */
NULL /* qi_mstat */
};
dm2s_wput, /* qi_putp */
dm2s_wsrv, /* qi_srvp */
NULL, /* qi_qopen */
NULL, /* qi_qlcose */
NULL, /* qi_qadmin */
&dm2s_module_info, /* qi_minfo */
NULL /* qi_mstat */
};
NULL,
};
"OPL Mbox to Serial Driver",
};
&modldrv,
};
/*
* _init - Module's init routine.
*/
int
_init(void)
{
int ret;
return (DDI_FAILURE);
}
}
return (ret);
}
/*
* _fini - Module's fini routine.
*/
int
_fini(void)
{
int ret;
return (ret);
}
return (ret);
}
/*
* _info - Module's info routine.
*/
int
{
}
/*
* dm2s_attach - Module's attach routine.
*/
int
{
int instance;
/* Only one instance is supported. */
if (instance != 0) {
return (DDI_FAILURE);
}
if (cmd != DDI_ATTACH) {
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
/*
* Get an interrupt block cookie corresponding to the
* interrupt priority of the event handler.
* Assert that the event priority is not re-defined to
* some higher priority.
*/
/* LINTED */
goto error;
}
(void *)dm2sp->ms_ibcookie);
goto error;
}
return (DDI_SUCCESS);
return (DDI_FAILURE);
}
/*
* dm2s_info - Module's info routine.
*/
/*ARGSUSED*/
int
{
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
} else {
ret = DDI_SUCCESS;
}
break;
case DDI_INFO_DEVT2INSTANCE:
ret = DDI_SUCCESS;
break;
default:
break;
}
return (ret);
}
/*
* dm2s_detach - Module's detach routine.
*/
int
{
int instance;
if (cmd != DDI_DETACH) {
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
/* Check if the mailbox is still in use. */
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* dm2s_open - Device open routine.
*
* Only one open supported. Clone open is not supported.
*/
/* ARGSUSED */
int
{
int ret = 0;
/* Clone open not supported */
return (ENOTSUP);
}
return (EBUSY);
}
return (ENODEV);
}
/* Only one open supported */
return (EBUSY);
}
/* Initialize the mailbox. */
return (ret);
}
if (ret == 0) {
}
return (ret);
}
/*
* dm2s_close - Device close routine.
*/
/* ARGSUSED */
int
{
/* Already closed once */
return (ENODEV);
}
/* Close the lower layer first */
/*
* Now we can assume that no asynchronous callbacks exist.
* Poison the stream head so that we can't be pushed again.
*/
if (dm2sp->ms_rbufcid != 0) {
dm2sp->ms_rbufcid = 0;
}
if (dm2sp->ms_rq_timeoutid != 0) {
dm2sp->ms_rq_timeoutid = 0;
}
if (dm2sp->ms_wq_timeoutid != 0) {
dm2sp->ms_wq_timeoutid = 0;
}
/*
* Now we can really mark it closed.
*/
return (0);
}
/*
* dm2s_rsrv - Streams read side service procedure.
*
* All messages are received in the service procedure
* only. This is done to simplify the streams synchronization.
*/
int
{
/* Receive if there are any messages waiting in the mailbox. */
/* Send the received messages up the stream. */
if (canputnext(rq)) {
} else {
break;
}
}
return (0);
}
/*
* dm2s_wsrv - Streams write side service procedure.
*
* All messages are transmitted in the service procedure
* only. This is done to simplify the streams synchronization.
*/
int
{
/* Lets cancel any timeouts waiting to be scheduled. */
if (dm2sp->ms_wq_timeoutid != 0) {
dm2sp->ms_wq_timeoutid = 0;
}
return (0);
}
/*
* dm2s_wput - Streams write side put routine.
*
* All M_DATA messages are queued so that they are transmitted in
* the service procedure. This is done to simplify the streams
* synchronization. Other messages are handled appropriately.
*/
int
{
return (ENODEV); /* Can't happen. */
}
case (M_DATA):
return (0);
}
}
/*
* Simply queue the message and handle it in the service
* procedure.
*/
return (0);
case (M_PROTO):
/* We don't expect this */
return (EINVAL);
case (M_IOCTL):
return (0);
}
/*
* No ioctls required to be supported by this driver, so
* return EINVAL for all ioctls.
*/
break;
case (M_CTL):
/*
* No M_CTL messages need to supported by this driver,
* so simply ignore them.
*/
break;
case (M_FLUSH):
}
} else {
}
break;
default:
}
return (0);
}
/*
* dm2s_cleanup - Cleanup routine.
*/
static void
{
}
}
/*
* dm2s_mbox_init - Mailbox specific initialization.
*/
static int
{
int ret;
/* Iterate until mailbox gets connected */
dm2s_event_handler, (void *)dm2sp);
if (ret != 0) {
("dm2s_mbox_init: failed ret =%d\n", ret));
} else {
/* Block until the mailbox is ready to communicate. */
(DM2S_MB_CONN | DM2S_MB_DISC))) {
/* interrupted */
break;
}
}
}
}
("dm2s_mbox_init: mbox DISC_ERROR\n"));
int, DM2S_MB_DISC);
}
return (ret);
}
/*
* If there was failure, then wait for
* DM2S_MB_TOUT secs and retry again.
*/
if (ret == 0) {
/* if interrupted, return immediately. */
("dm2s_mbox_init: interrupted\n"));
return (EINTR);
}
}
}
/*
* Obtain the max size of a single message.
* NOTE: There is no mechanism to update the
* upperlayers dynamically, so we expect this
* size to be atleast the default MTU size.
*/
}
if (ret != 0) {
}
return (ret);
}
/*
* dm2s_mbox_fini - Mailbox de-initialization.
*/
static void
{
int ret;
if (ret != 0) {
"Failed to close the Mailbox error =%d", ret);
}
}
}
/*
* dm2s_event_handler - Mailbox event handler.
*/
void
{
/*
* Ignore all events if the state flag indicates that the
* mailbox not initialized, this may happen during the close.
*/
("Event(0x%X) received - Mailbox not inited\n", event));
return;
}
switch (event) {
case SCF_MB_CONN_OK:
/*
* Now the mailbox is ready to use, lets wake up
* any one waiting for this event.
*/
break;
case SCF_MB_MSG_DATA:
if (!DM2S_MBOX_READY(dm2sp)) {
("Event(MSG_DATA) received - Mailbox not READY\n"));
break;
}
/*
* A message is available in the mailbox.
* Lets enable the read service procedure
* to receive this message.
*/
}
break;
case SCF_MB_SPACE:
if (!DM2S_MBOX_READY(dm2sp)) {
("Event(MB_SPACE) received - Mailbox not READY\n"));
break;
}
/*
* Now the mailbox is ready to transmit, lets
* schedule the write service procedure.
*/
}
break;
case SCF_MB_DISC_ERROR:
/*
* If it was previously connected,
* then send a hangup message.
*/
/*
* Send a hangup message to indicate
* disconnect event.
*/
}
} else {
/*
* Signal if the open is waiting for a
* connection.
*/
}
break;
default:
break;
}
}
/*
* dm2s_start - Start transmission function.
*
* Send all queued messages. If the mailbox is busy, then
* start a timeout as a polling mechanism. The timeout is useful
* to not rely entirely on the SCF_MB_SPACE event.
*/
void
{
int ret;
case M_DATA:
("dm2s_start: recoverable err=%d\n", ret));
/*
* Start a timeout to retry again.
*/
if (dm2sp->ms_wq_timeoutid == 0) {
dm2s_wq_timeout, (void *)dm2sp,
}
return;
} else if (ret != 0) {
/*
* An error occurred with the transmission,
* flush pending messages and initiate a
* hangup.
*/
("dm2s_start: hangup transmit err=%d\n",
ret));
}
break;
default:
/*
* At this point, we don't expect any other messages.
*/
break;
}
}
}
/*
* dm2s_receive - Read all messages from the mailbox.
*
* This function is called from the read service procedure, to
* receive the messages awaiting in the mailbox.
*/
void
{
int ret;
return;
}
/*
* As the number of messages in the mailbox are pretty limited,
* it is safe to process all messages in one loop.
*/
if (len == 0) {
break;
}
/*
* Start a bufcall so that we can retry again
* when memory becomes available.
*/
if (dm2sp->ms_rbufcid == 0) {
("dm2s_receive: qbufcall failed\n"));
/*
* if bufcall fails, start a timeout to
* initiate a re-try after some time.
*/
dm2s_rq_timeout, (void *)dm2sp,
}
break;
}
/*
*/
if (ret != 0) {
break;
}
/*
* Queue the messages in the rq, so that the service
* procedure handles sending the messages up the stream.
*/
}
/*
* Some thing went wrong, flush pending messages
* and initiate a hangup.
* Note: flushing the wq initiates a faster close.
*/
"condition - hangup ret=%d\n", ret));
}
}
/*
* dm2s_transmit - Transmit a message.
*/
int
{
int ret;
/*
* Free the message if the mailbox is not in the connected state.
*/
if (!DM2S_MBOX_READY(dm2sp)) {
return (EIO);
}
/*
* Size is too big to send, free the message.
*/
return (0);
}
DM2S_MAX_SG)) != 0) {
return (EAGAIN);
}
("dm2s_transmit: mailbox busy ret=%d\n", ret));
/*
* If maximum retries are reached, then free the
* message.
*/
("dm2s_transmit: freeing msg after max retries\n"));
dm2sp->ms_retries = 0;
return (0);
}
/*
* Queue it back, so that we can retry again.
*/
return (ret);
}
dm2sp->ms_retries = 0;
return (ret);
}
/*
* dm2s_bufcall_rcv - Bufcall callaback routine.
*
* It simply enables read side queue so that the service procedure
* can retry receive operation.
*/
void
{
dm2sp->ms_rbufcid = 0;
}
}
/*
* dm2s_rq_timeout - Timeout callback for the read side.
*
* It simply enables read side queue so that the service procedure
* can retry the receive operation.
*/
void
{
dm2sp->ms_rq_timeoutid = 0;
}
}
/*
* dm2s_wq_timeout - Timeout callback for the write.
*
* It simply enables write side queue so that the service procedure
* can retry the transmission operation.
*/
void
{
dm2sp->ms_wq_timeoutid = 0;
}
}
/*
* of a streams message.
*/
static int
{
num++;
}
/*
* enough, so lets pullup the msg.
*/
return (EAGAIN);
}
num = 1;
}
return (0);
}
/*
* dm2s_timeout_val -- Return appropriate timeout value.
*
* A small timeout value is returned for EBUSY and EAGAIN cases. This is
* because the condition is expected to be recovered sooner.
*
* A larger timeout value is returned for ENOSPC case, as the condition
* depends on the peer to release buffer space.
* NOTE: there will also be an event(SCF_MB_SPACE) but a timeout is
* used for reliability purposes.
*/
static clock_t
{
tval = DM2S_SM_TOUT;
} else {
tval = DM2S_LG_TOUT;
}
return (drv_usectohz(tval));
}
#ifdef DEBUG
static void
{
int i, j;
int nsg;
if (!(dm2s_debug & DBG_MESG))
return;
for (i = 0; i < len; i++) {
}
}
tlen = 0;
for (i = 0; i < len; ) {
for (j = 0; (j < BYTES_PER_LINE) &&
(i < len); j++, i++) {
datap[i]);
}
if (j != 0) {
DTRACE_PROBE1(dm2s_dump, unsigned char *,
bytestr);
}
}
tlen += i;
}
}
#endif /* DEBUG */