/*
* 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
*/
/*
* All Rights Reserved, Copyright (c) FUJITSU LIMITED 2006
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/prom_plat.h>
0,
16384,
14336,
2048
};
NULL,
NULL,
};
NULL,
};
NULL,
};
NULL,
NULL,
};
};
nulldev, /* cb_open */
nulldev, /* cb_close */
nodev, /* cb_strategy */
nodev, /* cb_print */
nodev, /* cb_dump */
nodev, /* cb_read */
nodev, /* cb_write */
nodev, /* cb_ioctl */
nodev, /* cb_devmap */
nodev, /* cb_mmap */
nodev, /* cb_segmap */
nochpoll, /* cb_chpoll */
ddi_prop_op, /* cb_prop_op */
(&oplmsu_info), /* cb_stream */
};
DEVO_REV, /* devo_rev */
0, /* devo_refcnt */
(oplmsu_getinfo), /* devo_getinfo */
(nulldev), /* devo_identify */
(nulldev), /* devo_probe */
(oplmsu_attach), /* devo_attach */
(oplmsu_detach), /* devo_detach */
(nodev), /* devo_reset */
&(cb_oplmsu_ops), /* devo_cb_ops */
NULL, /* devo_power */
ddi_quiesce_not_needed, /* dev_quiesce */
};
"OPL serial mux driver",
};
(void *)&modldrv,
};
#ifdef DEBUG
#endif
/* oplmsu_conf_st */
#define MSU_UNCONFIGURED 0
/*
* Locking hierarcy of oplmsu driver. This driver have 5 locks in uinst_t.
*
* Each mutex guards as follows.
*
* read lock : acquired if the member of uinst_t is refered only.
* write lock: acquired if the member of uinst_t is changed.
*
* uinst_t->u_lock: This mutex is normal mutex.
*
* uinst_t->l_lock: This mutex is normal mutex.
*
* uinst_t->c_lock: This mutex is normal mutex.
*
* oplmsu_bthrd_excl: This mutex is normal mutex.
* multiplexed STREAMS.
* This mutex is exclusively acquired with the above-mentioned 4 mutexes.
*
* To guard of the deadlock by cross locking, the base locking hierarcy
* is as follows:
*
* uisnt->lock ==> uinst->u_lock ==> uinst->l_lock ==> uinst->c_lock
*
*/
int
_init(void)
{
int rval;
/* Initialize R/W lock for uinst_t */
/* Initialize mutex for upath_t */
/* Initialize mutex for lpath_t */
/* Initialize mutex for ctrl_t */
/* Initialize mutex for protecting background thread */
/* Initialize condition variable */
if (rval != DDI_SUCCESS) {
}
return (rval);
}
int
_fini(void)
{
int rval;
if (rval == DDI_SUCCESS) {
}
return (rval);
}
int
{
}
/* ARGSUSED */
int
{
switch (cmd) {
case DDI_INFO_DEVT2DEVINFO :
rval = DDI_FAILURE;
} else {
}
break;
case DDI_INFO_DEVT2INSTANCE :
break;
default :
rval = DDI_FAILURE;
break;
}
return (rval);
}
int
{
int rval = 0;
int instance;
if (cmd == DDI_RESUME) {
return (DDI_SUCCESS);
}
if (cmd != DDI_ATTACH) {
return (DDI_FAILURE);
}
if (instance != 0) {
"Invaild instance => %d", instance);
return (DDI_FAILURE);
}
/* Create minor number for meta control node */
/* Create minor number for user access node */
/* Create minor node for user access */
DDI_NT_SERIAL, 0);
if (rval != DDI_SUCCESS) {
"ddi_create_minor_node failed. errno = %d", rval);
return (rval);
}
/* Create minor node for meta control */
if (rval != DDI_SUCCESS) {
"ddi_create_internal_pathname failed. errno = %d", rval);
return (rval);
}
/* Get each properties */
/*
* Initialize members of uinst_t
*/
#ifdef DEBUG
if (oplmsu_trace_on == MSU_TRACE_ON) {
/* Initialize mutex for msu_trc_t */
}
#endif
return (rval);
}
int
{
if (cmd == DDI_SUSPEND) {
return (DDI_SUCCESS);
}
if (cmd != DDI_DETACH) {
return (DDI_FAILURE);
}
/* Delete all upath_t */
/* Delete all lpath_t */
#ifdef DEBUG
if (oplmsu_trace_on == MSU_TRACE_ON) {
if (oplmsu_ltrc_top != NULL) {
(sizeof (msu_trc_t) * oplmsu_ltrc_size));
}
}
#endif
while (lpath) {
}
}
}
lpath = next_lpath;
}
return (DDI_SUCCESS);
}
/* ARGSUSED */
int
{
return (EINVAL);
}
/* Get minor device number */
/* Get node type */
return (EINVAL);
}
if ((node_flag == MSU_NODE_USER) &&
int cv_rval;
"oplmsu_conf_st = %x", oplmsu_conf_st));
if (oplmsu_conf_st == MSU_UNCONFIGURED) {
/* Start up background thread */
}
/*
* Wait with cv_wait_sig() until background thread is
* completed.
*/
while (oplmsu_conf_st == MSU_CONFIGURING) {
cv_rval =
if (cv_rval == 0) {
return (EINTR);
}
}
}
/*
* If the node which will open is meta-control-node or
* user-access-node, and q_ptr, this is queue_t queue
* table member, is not NULL, then oplmsu returns
* SUCCESS immidiately.
* This process is used to protect dual open.
*/
return (SUCCESS);
}
/*
* If the node which will open is User-Access-Node, and instance
* status of oplmsu is no ONLINE, then oplmsu_open process fails
* with return value 'EIO'.
*/
if ((node_flag == MSU_NODE_USER) &&
return (EIO);
}
/* Allocate kernel memory for ctrl_t */
/*
* Initialize members of ctrl_t
*/
oplmsu_queue_flag = 0;
} else { /* Meta control node */
}
return (SUCCESS);
}
/* ARGSUSED */
int
{
"close has already been completed"));
return (FAILURE);
}
if (node_flag > MSU_NODE_META) {
return (EINVAL);
}
/*
* Check that queue which is waiting for response from lower stream
* exist. If queue exists, oplmsu sets CV_SLEEP to sleep_flag.
*/
break;
}
}
/* If sleep_flag is not CV_SLEEP, oplmsu calls cv_wait. */
if (lpath) {
}
}
switch (node_flag) {
case MSU_NODE_USER : /* User access node */
oplmsu_queue_flag = 0;
break;
case MSU_NODE_META : /* Meta control node */
break;
default :
}
if (wbuf_id != 0) {
}
if (wtout_id != 0) {
}
/* Free kernel memory for ctrl_t */
return (SUCCESS);
}
/*
* Upper write put procedure
*/
int
{
return (SUCCESS);
}
return (SUCCESS);
}
/* Link high priority message to local queue */
} else {
}
return (SUCCESS);
}
/*
* Upper write service procedure
*/
int
{
int rval;
return (FAILURE);
}
/* Handle high priority message */
continue;
}
FAILURE) {
return (SUCCESS);
}
}
/* Handle normal priority message */
case M_IOCTL :
case I_PLINK :
}
break;
case I_PUNLINK :
}
break;
case TCSETS : /* FALLTHRU */
case TCSETSW : /* FALLTHRU */
case TCSETSF : /* FALLTHRU */
case TIOCMSET : /* FALLTHRU */
case TIOCSPPS : /* FALLTHRU */
case TIOCSWINSZ : /* FALLTHRU */
case TIOCSSOFTCAR :
break;
default :
break;
}
break;
default :
break;
}
break;
}
}
return (SUCCESS);
}
/*
* Lower write service procedure
*/
int
{
continue;
}
if (canputnext(dst_queue)) {
} else {
break;
}
}
}
return (SUCCESS);
}
/*
* Lower read put procedure
*/
int
{
return (SUCCESS);
}
return (SUCCESS);
}
/* Link high priority message to local queue */
} else {
}
return (SUCCESS);
}
/*
* Lower read service procedure
*/
int
{
int rval;
return (FAILURE);
}
/* Handle normal priority message */
}
case M_DATA :
if ((abort_enable == KIOCABORTALTERNATE) &&
abort_sequence_enter((char *)
NULL);
= oplmsu_uinst->abts;
break;
}
} else {
*oplmsu_uinst->abts) ?
}
rx_char++;
}
}
if (aborted) {
continue;
}
/*
* When 1st byte of the received M_DATA is XON or,
* 1st byte is XOFF and 2nd byte is XON.
*/
return (SUCCESS);
}
} else {
rval =
return (SUCCESS);
}
}
break;
case M_BREAK :
== 0) {
if ((abort_enable != KIOCABORTALTERNATE) &&
abort_sequence_enter((char *)NULL);
}
break;
}
/* FALLTHRU */
default :
break;
}
}
return (SUCCESS);
}
/*
* Upper read service procedure
*/
int
{
int res_chk = 0;
res_chk = 1;
}
while (lpath) {
}
if (res_chk == 1) {
}
}
res_chk = 0;
}
continue;
}
if (canputnext(dst_queue)) {
} else {
break;
}
}
}
return (SUCCESS);
}
int
{
int rval;
/* Allocate LDI identifier */
if (rval != 0) {
"ldi_ident_from_dip failed. errno = %d", rval);
return (rval);
}
/* Open oplmsu(meta ctrl node) */
rval =
if (rval != 0) {
"ldi_open_by_dev failed. errno = %d", rval);
}
return (rval);
}
int
{
int param;
int rval;
/* Create physical path-name for serial */
/* Allocate LDI identifier */
if (rval != 0) {
return (rval);
}
/* Open serial */
if (rval != 0) {
return (rval);
}
/* Try to remove the top module from the stream */
param = 0;
== 0) {
continue;
}
/* Issue ioctl(I_PLINK) */
param = 0;
if (rval != 0) {
}
return (rval);
}
int
{
while (lpath) {
break;
}
}
}
return (rval);
}
int
{
int len;
int instance;
int lnk_id = 0;
int param = 0;
int rval;
/* Get instance for serial */
/* Get current number of paths */
/* Check specified upath_t */
while (upath) {
break;
}
}
"Instance %d already exist", instance);
return (EINVAL);
}
/* Open oplmsu */
if (rval != 0) {
"msu open failed. errno = %d", rval);
return (rval);
}
/* Connect two streams */
if (rval != 0) {
"i_plink failed. errno = %d", rval);
return (rval);
}
if (rval != 0) {
"Link id %d is not found", lnk_id);
/* Issue ioctl(I_PUNLINK) */
return (rval);
}
/* Add the path */
if (rval != 0) {
"Failed to add the path. errno = %d", rval);
/* Issue ioctl(I_PUNLINK) */
return (rval);
}
/* Start to use the path */
if (rval != 0) {
"Failed to start the path. errno = %d", rval);
/* Delete the path */
if ((oplmsu_config_del(mpath)) == 0) {
/* Issue ioctl(I_PUNLINK) */
}
}
/* Close oplmsu */
return (rval);
}
int
{
int len;
int instance;
int count = 0;
int param = 0;
int status;
int rval;
/* Get instance for serial */
/* Get current number of paths */
/* Check specified upath_t */
while (upath) {
/* Save status of specified path */
}
count += 1;
}
if (count <= 1) {
"Instance %d is last path", instance);
} else {
"Instance %d doesn't find", instance);
}
return (EINVAL);
}
/* Check status of specified path */
/* Stop to use the path */
if (rval != 0) {
"Failed to stop the path. errno = %d", rval);
return (rval);
}
}
/* Prepare to unlink the path */
if (rval != 0) {
"Failed to disconnect the path. errno = %d", rval);
return (rval);
}
while (lpath) {
break;
}
}
return (EINVAL);
}
/* Open oplmsu */
if (rval != 0) {
"msu open failed. errno = %d", rval);
return (rval);
}
/* Issue ioctl(I_PUNLINK) */
if (rval != 0) {
"ioctl(I_PUNLINK) failed. errno = %d", rval);
return (rval);
}
/* Close oplmsu(meta node) */
/* Delete the path */
if (rval != 0) {
"Failed to delete the path. errno = %d", rval);
}
return (rval);
}
/*
* The ebus and the serial device path under a given CMU_CH chip
* is expected to be always at the same address. So, it is safe
* to hard-code the pathnames as below.
*/
/*
* Given the CMU_CH dip, find the serial device dip.
*/
{
"ebus_dip = %p", (void *)ebus_dip));
"ser_dip = %p", (void *)ser_dip));
}
return (ser_dip);
}
/*
* Find all console related serial devices.
*/
int
{
int circ;
int count = 0;
char *namep;
root_dip = ddi_root_node();
continue;
}
#ifdef DEBUG
continue;
}
#else
continue;
#endif
}
/*
* Online the cmuch_dip so that its in the right state
* to get the complete path, that is both name and address.
*/
(void) ndi_devi_online(cmuch_dip, 0);
"oplmsu: find-serial: cmu-ch path => %s", pathname));
/*
* Call ddi_pathname_to_dev_t to forceload and attach
* the required drivers.
*/
"dev_t = %lx", devt));
wrk_ser_dl = (ser_devl_t *)
count += 1;
}
*ser_dl = wrk_ser_dl;
}
}
return (count);
}
/* Configure STREAM */
void
{
int *plink_id;
int size;
int i;
int param;
int connected = 0;
int devcnt = 0;
int rval;
"oplmsu: conf-stream: stream configuration start!"));
/* Find serial devices */
"Discovered serial device = %d", devcnt);
return;
}
/* Open oplmsu */
if (rval != 0) {
"msu open failed. errno = %d", rval);
return;
}
for (i = 0; i < devcnt; i++) {
/* Connect two streams */
if (rval != 0) {
"i_plink failed. errno = %d", rval);
continue;
}
if (rval != 0) {
"Link id %d is not found", plink_id[i]);
/* Issue ioctl(I_PUNLINK) */
continue;
}
mdev++;
connected++;
}
if (connected == 0) {
"Connected paths = %d", connected);
return;
}
/* Setup all structure */
if (rval != 0) {
"Failed to create all paths. errno = %d", rval);
return;
}
/* Start to use all paths */
if (rval != 0) {
"Failed to start all paths. errno = %d", rval);
/* Delete the path */
if (rval == 0) {
}
}
}
void
{
int i;
int param = 0;
for (i = 0; i < devcnt; i++) {
if (plink_id[i] == 0) {
continue;
}
/* Issue ioctl(I_PUNLINK) */
}
}
void
{
if (oplmsu_conf_st == MSU_CONFIGURING) {
}
if (oplmsu_bthrd_id != NULL) {
}
thread_exit();
}
int
{
int instance;
int lsb;
FAILURE);
return (lsb);
}
while (lpath) {
break;
}
}
return (ENODEV);
}
/*
* Initialize members of upath_t
*/
MSU_STOP);
return (SUCCESS);
}
/* Setup new upper instance structure */
int
{
int i;
return (EINVAL);
}
"Other processing is using this device");
return (EBUSY);
}
/*
* Because the OPLMSU instance already exists when the upper path
* table exists, the configure_new processing cannot be done.
*/
return (EINVAL);
}
/*
* Because the config_new processing has already been done
* if oplmsu_uinst->path_num isn't -1, this processing cannot be
* continued.
*/
"conf-new processing has already been completed");
return (EINVAL);
}
/*
* Only the number of specified paths makes the upper path
* information tables.
*/
/*
* Associate upper path information table with lower path
* information table.
*
* If the upper path information table and the lower path
* information table cannot be associated, the link list of
* the upper path information table is released.
*/
"Failed to create upath %d", rval);
return (rval);
}
mdev++;
}
/*
* Setup members of uinst_t
*/
return (SUCCESS);
}
/* Add path information */
int
{
int instance;
"conf-new processing has not been completed yet");
return (EINVAL);
}
"Proper upath_t doesn't find");
return (EINVAL);
}
"Failed to create upath %d", rval);
return (rval);
}
return (SUCCESS);
}
/* Delete each path information */
int
{
int use_flag;
int i;
"Proper upath_t doesn't find");
mdev++;
continue;
}
"Other processing is using this device");
mdev++;
continue;
}
"Status of path is improper");
mdev++;
continue;
}
} else {
"Other processing is using lower path");
mdev++;
continue;
}
mdev++;
continue;
}
}
mdev++;
}
return (rval);
}
/* Stop to use the path */
int
{
int use_flag;
"oplmsu: conf-stop: config_stop(%d) called", pathnum));
if (pathnum == MSU_PATH_ALL) {
"All path can't be transferred to the status of "
"'Offline:stop'");
return (EINVAL);
}
"Proper upath_t doesn't find");
return (ENODEV);
}
"Proper lpath_t doesn't exist");
return (ENODEV);
}
/* Check status of lpath_t */
"Other processing is using lower path");
return (EBUSY);
}
return (EIO);
return (SUCCESS);
return (SUCCESS);
"Alternate upper path doesn't find"));
return (EINVAL);
}
return (ENOSR);
}
SUCCESS) {
return (ENOSR);
}
"Other processing is using alternate lower path");
return (EBUSY);
}
/* termios is not held. Change alternate path to MSU_ACTIVE */
oplmsu_queue_flag = 1;
}
/* Make M_FLUSH and send to alternate path */
/* Change status of alternate path */
/* Notify of the active path changing */
(void) prom_opl_switch_console(
/* Send XON to notify active path */
/* Send XOFF to notify all standby paths */
/* Switch active path of oplmsu */
/* Restart queuing of user access node */
oplmsu_queue_flag = 0;
}
/* Stop previous active path */
return (SUCCESS);
}
/* Send termios information to alternate path */
oplmsu_queue_flag = 1;
}
/* Wait for the completion of path switching */
}
return (SUCCESS);
} else {
return (FAILURE);
}
/* NOTREACHED */
} else {
"Status of path is improper");
return (EINVAL);
}
/* NOTREACHED */
}
/* Start to use path */
int
{
int msu_tty_port;
"oplmsu: conf-start: config_start(%d) called", pathnum));
if (oplmsu_get_inst_status() == INST_STAT_BUSY) {
return (EBUSY);
}
if (pathnum == MSU_PATH_ALL) {
(void) oplmsu_search_min_stop_path();
}
continue;
}
"Proper lpath_t doesn't exist");
return (EINVAL);
}
return (SUCCESS);
}
/*
* with PATH_ALL
*/
"Proper lpath_t doesn't exist"));
continue;
}
/* Notify of the active path changing */
/* Send XON to notify active path */
} else {
}
}
if (altn_upath) {
/* Notify of the active path changing */
(void) prom_opl_switch_console(
if (altn_lpath) {
/* Send XON to notify active path */
(void) oplmsu_cmn_put_xoffxon(dst_queue,
} else {
"Proper alternate lpath_t doesn't exist");
}
} else {
"Proper alternate upath_t doesn't exist");
}
}
/* Send XOFF to notify all standby paths */
/* Change active path of oplmsu */
return (SUCCESS);
}
/* Prepare of unlink path */
int
{
int use_flag;
"oplmsu: conf-disc: config_disc(%d) called", pathnum));
"Proper upath_t doesn't find");
return (EINVAL);
}
return (SUCCESS);
"Status of path is improper");
return (EINVAL);
}
"Proper lpath_t doesn't exist");
return (ENODEV);
}
/* Check lower path status */
"Other processing is using lower path");
return (EBUSY);
}
return (SUCCESS);
}