#include <sys/errno.h>
#include <sys/modctl.h>
#include <sys/stat.h>
#include <sys/kmem.h>
#include <sys/ksynch.h>
#include <sys/stream.h>
#include <sys/stropts.h>
#include <sys/termio.h>
#include <sys/ddi.h>
#include <sys/file.h>
#include <sys/disp.h>
#include <sys/sunddi.h>
#include <sys/sunldi.h>
#include <sys/sunndi.h>
#include <sys/kbio.h>
#include <sys/prom_plat.h>
#include <sys/oplmsu/oplmsu.h>
#include <sys/oplmsu/oplmsu_proto.h>
extern int ddi_create_internal_pathname(dev_info_t *, char *, int, minor_t);
#define MOD_ID 0xe145
#define MOD_NAME "oplmsu"
#define META_NAME "oplmsu"
#define USER_NAME "a"
struct module_info oplmsu_mod_info = {
MOD_ID,
MOD_NAME,
0,
16384,
14336,
2048
};
struct qinit oplmsu_urinit = {
NULL,
oplmsu_ursrv,
oplmsu_open,
oplmsu_close,
NULL,
&oplmsu_mod_info,
NULL
};
struct qinit oplmsu_uwinit = {
oplmsu_uwput,
oplmsu_uwsrv,
oplmsu_open,
oplmsu_close,
NULL,
&oplmsu_mod_info,
NULL
};
struct qinit oplmsu_lrinit = {
oplmsu_lrput,
oplmsu_lrsrv,
oplmsu_open,
oplmsu_close,
NULL,
&oplmsu_mod_info,
NULL
};
struct qinit oplmsu_lwinit = {
NULL,
oplmsu_lwsrv,
oplmsu_open,
oplmsu_close,
NULL,
&oplmsu_mod_info,
NULL
};
struct streamtab oplmsu_info = {
&oplmsu_urinit,
&oplmsu_uwinit,
&oplmsu_lrinit,
&oplmsu_lwinit
};
static struct cb_ops cb_oplmsu_ops = {
nulldev,
nulldev,
nodev,
nodev,
nodev,
nodev,
nodev,
nodev,
nodev,
nodev,
nodev,
nochpoll,
ddi_prop_op,
(&oplmsu_info),
(int)(D_NEW|D_MP|D_HOTPLUG)
};
static struct dev_ops oplmsu_ops = {
DEVO_REV,
0,
(oplmsu_getinfo),
(nulldev),
(nulldev),
(oplmsu_attach),
(oplmsu_detach),
(nodev),
&(cb_oplmsu_ops),
(struct bus_ops *)NULL,
NULL,
ddi_quiesce_not_needed,
};
struct modldrv modldrv = {
&mod_driverops,
"OPL serial mux driver",
&oplmsu_ops
};
struct modlinkage modlinkage = {
MODREV_1,
(void *)&modldrv,
NULL
};
uinst_t oplmsu_uinst_local;
uinst_t *oplmsu_uinst = &oplmsu_uinst_local;
int oplmsu_queue_flag;
int oplmsu_check_su;
#ifdef DEBUG
int oplmsu_debug_mode = 0;
int oplmsu_trace_on;
uint_t oplmsu_ltrc_size;
msu_trc_t *oplmsu_ltrc_top;
msu_trc_t *oplmsu_ltrc_tail;
msu_trc_t *oplmsu_ltrc_cur;
ulong_t oplmsu_ltrc_ccnt;
kmutex_t oplmsu_ltrc_lock;
#endif
#define MSU_CONFIGURED 2
#define MSU_CONFIGURING 1
#define MSU_UNCONFIGURED 0
static kmutex_t oplmsu_bthrd_excl;
static kthread_id_t oplmsu_bthrd_id = NULL;
static int oplmsu_conf_st = MSU_UNCONFIGURED;
static kcondvar_t oplmsu_conf_cv;
int
_init(void)
{
int rval;
rw_init(&oplmsu_uinst->lock, "uinst rwlock", RW_DRIVER, NULL);
mutex_init(&oplmsu_uinst->u_lock, "upath lock", MUTEX_DRIVER, NULL);
mutex_init(&oplmsu_uinst->l_lock, "lpath lock", MUTEX_DRIVER, NULL);
mutex_init(&oplmsu_uinst->c_lock, "ctrl lock", MUTEX_DRIVER, NULL);
mutex_init(&oplmsu_bthrd_excl, NULL, MUTEX_DRIVER, NULL);
cv_init(&oplmsu_conf_cv, NULL, CV_DRIVER, NULL);
rval = mod_install(&modlinkage);
if (rval != DDI_SUCCESS) {
cv_destroy(&oplmsu_conf_cv);
mutex_destroy(&oplmsu_bthrd_excl);
mutex_destroy(&oplmsu_uinst->c_lock);
mutex_destroy(&oplmsu_uinst->l_lock);
mutex_destroy(&oplmsu_uinst->u_lock);
rw_destroy(&oplmsu_uinst->lock);
}
return (rval);
}
int
_fini(void)
{
int rval;
rval = mod_remove(&modlinkage);
if (rval == DDI_SUCCESS) {
cv_destroy(&oplmsu_conf_cv);
mutex_destroy(&oplmsu_bthrd_excl);
mutex_destroy(&oplmsu_uinst->c_lock);
mutex_destroy(&oplmsu_uinst->l_lock);
mutex_destroy(&oplmsu_uinst->u_lock);
rw_destroy(&oplmsu_uinst->lock);
}
return (rval);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
int
oplmsu_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
{
dev_t dev = (dev_t)arg;
minor_t inst;
int rval = DDI_SUCCESS;
switch (cmd) {
case DDI_INFO_DEVT2DEVINFO :
if (oplmsu_uinst->msu_dip == NULL) {
rval = DDI_FAILURE;
} else {
*resultp = oplmsu_uinst->msu_dip;
}
break;
case DDI_INFO_DEVT2INSTANCE :
inst = getminor(dev) & ~(META_NODE_MASK|USER_NODE_MASK);
*resultp = (void *)(uintptr_t)inst;
break;
default :
rval = DDI_FAILURE;
break;
}
return (rval);
}
int
oplmsu_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
minor_t meta_minor, user_minor;
int rval = 0;
int instance;
#define CNTRL(c) ((c) & 037)
char abt_ch_seq[3] = { '\r', '~', CNTRL('b') };
if (cmd == DDI_RESUME) {
return (DDI_SUCCESS);
}
if (cmd != DDI_ATTACH) {
return (DDI_FAILURE);
}
instance = ddi_get_instance(dip);
if (instance != 0) {
cmn_err(CE_WARN, "oplmsu: attach: "
"Invaild instance => %d", instance);
return (DDI_FAILURE);
}
meta_minor = instance | META_NODE_MASK;
user_minor = instance | USER_NODE_MASK;
rval = ddi_create_minor_node(dip, USER_NAME, S_IFCHR, user_minor,
DDI_NT_SERIAL, 0);
if (rval != DDI_SUCCESS) {
cmn_err(CE_WARN, "oplmsu: attach: "
"ddi_create_minor_node failed. errno = %d", rval);
ddi_remove_minor_node(dip, NULL);
return (rval);
}
rval = ddi_create_internal_pathname(dip, META_NAME, S_IFCHR,
meta_minor);
if (rval != DDI_SUCCESS) {
cmn_err(CE_WARN, "oplmsu: attach: "
"ddi_create_internal_pathname failed. errno = %d", rval);
ddi_remove_minor_node(dip, NULL);
return (rval);
}
rw_enter(&oplmsu_uinst->lock, RW_WRITER);
oplmsu_check_su = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
(DDI_PROP_DONTPASS|DDI_PROP_NOTPROM), "check-superuser", 1);
oplmsu_uinst->inst_status = INST_STAT_UNCONFIGURED;
oplmsu_uinst->path_num = UNDEFINED;
oplmsu_uinst->msu_dip = dip;
(void) strcpy(oplmsu_uinst->abts, abt_ch_seq);
#ifdef DEBUG
oplmsu_trace_on = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
(DDI_PROP_DONTPASS|DDI_PROP_NOTPROM), "trace-mode", 1);
oplmsu_ltrc_size = (uint_t)ddi_prop_get_int(DDI_DEV_T_ANY, dip,
(DDI_PROP_DONTPASS|DDI_PROP_NOTPROM), "trace-bufsize", 128);
if (oplmsu_trace_on == MSU_TRACE_ON) {
mutex_init(&oplmsu_ltrc_lock, "trc lock", MUTEX_DRIVER, NULL);
mutex_enter(&oplmsu_ltrc_lock);
oplmsu_ltrc_top = (msu_trc_t *)kmem_zalloc(
(sizeof (msu_trc_t) * oplmsu_ltrc_size), KM_SLEEP);
oplmsu_ltrc_cur = (msu_trc_t *)(oplmsu_ltrc_top - 1);
oplmsu_ltrc_tail =
(msu_trc_t *)(oplmsu_ltrc_top + (oplmsu_ltrc_size - 1));
mutex_exit(&oplmsu_ltrc_lock);
}
#endif
rw_exit(&oplmsu_uinst->lock);
ddi_report_dev(dip);
return (rval);
}
int
oplmsu_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
lpath_t *lpath, *next_lpath;
if (cmd == DDI_SUSPEND) {
return (DDI_SUCCESS);
}
if (cmd != DDI_DETACH) {
return (DDI_FAILURE);
}
rw_enter(&oplmsu_uinst->lock, RW_WRITER);
oplmsu_delete_upath_info();
mutex_enter(&oplmsu_uinst->l_lock);
lpath = oplmsu_uinst->first_lpath;
oplmsu_uinst->first_lpath = NULL;
oplmsu_uinst->last_lpath = NULL;
mutex_exit(&oplmsu_uinst->l_lock);
#ifdef DEBUG
if (oplmsu_trace_on == MSU_TRACE_ON) {
mutex_enter(&oplmsu_ltrc_lock);
if (oplmsu_ltrc_top != NULL) {
kmem_free(oplmsu_ltrc_top,
(sizeof (msu_trc_t) * oplmsu_ltrc_size));
}
oplmsu_ltrc_top = NULL;
oplmsu_ltrc_cur = NULL;
oplmsu_ltrc_tail = NULL;
mutex_exit(&oplmsu_ltrc_lock);
mutex_destroy(&oplmsu_ltrc_lock);
}
#endif
rw_exit(&oplmsu_uinst->lock);
while (lpath) {
if (lpath->rbuf_id) {
unbufcall(lpath->rbuf_id);
}
if (lpath->rtout_id) {
(void) untimeout(lpath->rtout_id);
}
if (lpath->rbuftbl) {
kmem_free(lpath->rbuftbl, sizeof (struct buf_tbl));
}
cv_destroy(&lpath->sw_cv);
next_lpath = lpath->l_next;
kmem_free(lpath, sizeof (lpath_t));
lpath = next_lpath;
}
ddi_remove_minor_node(dip, NULL);
return (DDI_SUCCESS);
}
int
oplmsu_open(queue_t *urq, dev_t *dev, int oflag, int sflag, cred_t *cred_p)
{
ctrl_t *ctrl;
minor_t mindev = 0;
minor_t qmindev = 0;
major_t majdev;
ulong_t node_flag;
DBG_PRINT((CE_NOTE, "oplmsu: open: "
"devt = 0x%lx, sflag = 0x%x", *dev, sflag));
if (sflag == CLONEOPEN) {
return (EINVAL);
}
qmindev = (minor_t)getminor(*dev);
node_flag = MSU_NODE_TYPE(qmindev);
if ((node_flag != MSU_NODE_USER) && (node_flag != MSU_NODE_META)) {
return (EINVAL);
}
mutex_enter(&oplmsu_bthrd_excl);
if ((node_flag == MSU_NODE_USER) &&
(oplmsu_conf_st != MSU_CONFIGURED)) {
int cv_rval;
DBG_PRINT((CE_NOTE, "oplmsu: open: "
"oplmsu_conf_st = %x", oplmsu_conf_st));
if (oplmsu_conf_st == MSU_UNCONFIGURED) {
oplmsu_conf_st = MSU_CONFIGURING;
oplmsu_bthrd_id = thread_create(NULL, 2 * DEFAULTSTKSZ,
oplmsu_setup, (void *)oplmsu_uinst, 0, &p0, TS_RUN,
minclsyspri);
}
while (oplmsu_conf_st == MSU_CONFIGURING) {
cv_rval =
cv_wait_sig(&oplmsu_conf_cv, &oplmsu_bthrd_excl);
if (cv_rval == 0) {
mutex_exit(&oplmsu_bthrd_excl);
return (EINTR);
}
}
}
mutex_exit(&oplmsu_bthrd_excl);
rw_enter(&oplmsu_uinst->lock, RW_WRITER);
if ((urq != NULL) && (urq->q_ptr != NULL)) {
rw_exit(&oplmsu_uinst->lock);
return (SUCCESS);
}
if ((node_flag == MSU_NODE_USER) &&
(oplmsu_uinst->inst_status != INST_STAT_ONLINE)) {
rw_exit(&oplmsu_uinst->lock);
return (EIO);
}
mindev |= qmindev;
majdev = getmajor(*dev);
*dev = makedevice(majdev, mindev);
ctrl = (ctrl_t *)kmem_zalloc(sizeof (ctrl_t), KM_SLEEP);
ctrl->minor = (minor_t)mindev;
ctrl->queue = urq;
ctrl->sleep_flag = CV_WAKEUP;
ctrl->node_type = node_flag;
ctrl->wbuftbl =
(struct buf_tbl *)kmem_zalloc(sizeof (struct buf_tbl), KM_SLEEP);
cv_init(&ctrl->cvp, "oplmsu ctrl_tbl condvar", CV_DRIVER, NULL);
mutex_enter(&oplmsu_uinst->c_lock);
if (node_flag == MSU_NODE_USER) {
oplmsu_uinst->user_ctrl = ctrl;
oplmsu_queue_flag = 0;
} else {
oplmsu_uinst->meta_ctrl = ctrl;
}
RD(urq)->q_ptr = ctrl;
WR(urq)->q_ptr = ctrl;
mutex_exit(&oplmsu_uinst->c_lock);
rw_exit(&oplmsu_uinst->lock);
OPLMSU_TRACE(urq, (mblk_t *)node_flag, MSU_TRC_OPN);
qprocson(urq);
return (SUCCESS);
}
int
oplmsu_close(queue_t *urq, int flag, cred_t *cred_p)
{
ctrl_t *ctrl;
minor_t qmindev = 0;
lpath_t *lpath;
ulong_t node_flag;
bufcall_id_t wbuf_id;
timeout_id_t wtout_id;
rw_enter(&oplmsu_uinst->lock, RW_READER);
mutex_enter(&oplmsu_uinst->l_lock);
mutex_enter(&oplmsu_uinst->c_lock);
if ((ctrl = urq->q_ptr) == NULL) {
mutex_exit(&oplmsu_uinst->c_lock);
mutex_exit(&oplmsu_uinst->l_lock);
rw_exit(&oplmsu_uinst->lock);
DBG_PRINT((CE_NOTE, "oplmsu: close: "
"close has already been completed"));
return (FAILURE);
}
qmindev = ctrl->minor;
DBG_PRINT((CE_NOTE, "oplmsu: close: ctrl->minor = 0x%x", qmindev));
node_flag = MSU_NODE_TYPE(qmindev);
if (node_flag > MSU_NODE_META) {
mutex_exit(&oplmsu_uinst->c_lock);
mutex_exit(&oplmsu_uinst->l_lock);
rw_exit(&oplmsu_uinst->lock);
return (EINVAL);
}
for (lpath = oplmsu_uinst->first_lpath; lpath; ) {
if (((RD(urq) == lpath->hndl_uqueue) ||
(WR(urq) == lpath->hndl_uqueue)) &&
(lpath->hndl_mp != NULL)) {
ctrl->sleep_flag = CV_SLEEP;
break;
}
lpath = lpath->l_next;
}
mutex_exit(&oplmsu_uinst->l_lock);
rw_exit(&oplmsu_uinst->lock);
if (lpath) {
while (ctrl->sleep_flag != CV_WAKEUP) {
cv_wait(&ctrl->cvp, &oplmsu_uinst->c_lock);
}
}
flushq(RD(urq), FLUSHALL);
flushq(WR(urq), FLUSHALL);
mutex_exit(&oplmsu_uinst->c_lock);
qprocsoff(urq);
rw_enter(&oplmsu_uinst->lock, RW_WRITER);
switch (node_flag) {
case MSU_NODE_USER :
oplmsu_uinst->user_ctrl = NULL;
oplmsu_queue_flag = 0;
break;
case MSU_NODE_META :
oplmsu_uinst->meta_ctrl = NULL;
break;
default :
cmn_err(CE_WARN, "oplmsu: close: node_flag = 0x%lx", node_flag);
}
ctrl->minor = 0;
ctrl->queue = NULL;
wbuf_id = ctrl->wbuf_id;
wtout_id = ctrl->wtout_id;
ctrl->wbuf_id = 0;
ctrl->wtout_id = 0;
cv_destroy(&ctrl->cvp);
kmem_free(ctrl->wbuftbl, sizeof (struct buf_tbl));
ctrl->wbuftbl = NULL;
RD(urq)->q_ptr = NULL;
WR(urq)->q_ptr = NULL;
rw_exit(&oplmsu_uinst->lock);
if (wbuf_id != 0) {
unbufcall(wbuf_id);
}
if (wtout_id != 0) {
(void) untimeout(wtout_id);
}
kmem_free(ctrl, sizeof (ctrl_t));
OPLMSU_TRACE(urq, (mblk_t *)node_flag, MSU_TRC_CLS);
return (SUCCESS);
}
int
oplmsu_uwput(queue_t *uwq, mblk_t *mp)
{
if (mp == NULL) {
return (SUCCESS);
}
if ((uwq == NULL) || (uwq->q_ptr == NULL)) {
freemsg(mp);
return (SUCCESS);
}
OPLMSU_TRACE(uwq, mp, MSU_TRC_UI);
rw_enter(&oplmsu_uinst->lock, RW_READER);
if (mp->b_datap->db_type == M_FLUSH) {
oplmsu_wcmn_flush_hndl(uwq, mp, RW_READER);
} else if (mp->b_datap->db_type >= QPCTL) {
ctrl_t *ctrl;
mutex_enter(&oplmsu_uinst->c_lock);
ctrl = (ctrl_t *)uwq->q_ptr;
oplmsu_link_high_primsg(&ctrl->first_upri_hi,
&ctrl->last_upri_hi, mp);
mutex_exit(&oplmsu_uinst->c_lock);
oplmsu_wcmn_high_qenable(WR(uwq), RW_READER);
} else {
(void) putq(WR(uwq), mp);
}
rw_exit(&oplmsu_uinst->lock);
return (SUCCESS);
}
int
oplmsu_uwsrv(queue_t *uwq)
{
struct iocblk *iocp = NULL;
mblk_t *mp = NULL;
int rval;
if ((uwq == NULL) || (uwq->q_ptr == NULL)) {
return (FAILURE);
}
rw_enter(&oplmsu_uinst->lock, RW_READER);
while (mp = oplmsu_wcmn_high_getq(uwq)) {
if (mp->b_datap->db_type == M_FLUSH) {
oplmsu_wcmn_flush_hndl(uwq, mp, RW_READER);
continue;
}
if (oplmsu_wcmn_through_hndl(uwq, mp, MSU_HIGH, RW_READER) ==
FAILURE) {
rw_exit(&oplmsu_uinst->lock);
return (SUCCESS);
}
}
rw_exit(&oplmsu_uinst->lock);
while (mp = getq(uwq)) {
rval = SUCCESS;
switch (mp->b_datap->db_type) {
case M_IOCTL :
iocp = (struct iocblk *)mp->b_rptr;
switch (iocp->ioc_cmd) {
case I_PLINK :
if (oplmsu_cmn_pullup_msg(uwq, mp) != FAILURE) {
rval = oplmsu_uwioctl_iplink(uwq, mp);
}
break;
case I_PUNLINK :
if (oplmsu_cmn_pullup_msg(uwq, mp) != FAILURE) {
rval = oplmsu_uwioctl_ipunlink(uwq, mp);
}
break;
case TCSETS :
case TCSETSW :
case TCSETSF :
case TIOCMSET :
case TIOCSPPS :
case TIOCSWINSZ :
case TIOCSSOFTCAR :
rval = oplmsu_uwioctl_termios(uwq, mp);
break;
default :
rw_enter(&oplmsu_uinst->lock, RW_READER);
rval = oplmsu_wcmn_through_hndl(uwq, mp,
MSU_NORM, RW_READER);
rw_exit(&oplmsu_uinst->lock);
break;
}
break;
default :
rw_enter(&oplmsu_uinst->lock, RW_READER);
rval = oplmsu_wcmn_through_hndl(uwq, mp, MSU_NORM,
RW_READER);
rw_exit(&oplmsu_uinst->lock);
break;
}
if (rval == FAILURE) {
break;
}
}
return (SUCCESS);
}
int
oplmsu_lwsrv(queue_t *lwq)
{
mblk_t *mp;
queue_t *dst_queue;
lpath_t *lpath;
rw_enter(&oplmsu_uinst->lock, RW_READER);
while (mp = getq(lwq)) {
if (mp->b_datap->db_type >= QPCTL) {
rw_exit(&oplmsu_uinst->lock);
OPLMSU_TRACE(WR(lwq), mp, MSU_TRC_LO);
putnext(WR(lwq), mp);
rw_enter(&oplmsu_uinst->lock, RW_READER);
continue;
}
dst_queue = WR(lwq);
if (canputnext(dst_queue)) {
rw_exit(&oplmsu_uinst->lock);
OPLMSU_TRACE(dst_queue, mp, MSU_TRC_LO);
putnext(dst_queue, mp);
rw_enter(&oplmsu_uinst->lock, RW_READER);
} else {
(void) putbq(WR(lwq), mp);
break;
}
}
mutex_enter(&oplmsu_uinst->l_lock);
lpath = (lpath_t *)lwq->q_ptr;
if (lpath->uwq_flag != 0) {
qenable(WR(lpath->uwq_queue));
lpath->uwq_flag = 0;
lpath->uwq_queue = NULL;
}
mutex_exit(&oplmsu_uinst->l_lock);
rw_exit(&oplmsu_uinst->lock);
return (SUCCESS);
}
int
oplmsu_lrput(queue_t *lrq, mblk_t *mp)
{
if (mp == NULL) {
return (SUCCESS);
}
if ((lrq == NULL) || (lrq->q_ptr == NULL)) {
freemsg(mp);
return (SUCCESS);
}
OPLMSU_TRACE(lrq, mp, MSU_TRC_LI);
if (mp->b_datap->db_type == M_FLUSH) {
rw_enter(&oplmsu_uinst->lock, RW_READER);
oplmsu_rcmn_flush_hndl(lrq, mp);
rw_exit(&oplmsu_uinst->lock);
} else if (mp->b_datap->db_type >= QPCTL) {
lpath_t *lpath;
rw_enter(&oplmsu_uinst->lock, RW_READER);
mutex_enter(&oplmsu_uinst->l_lock);
lpath = lrq->q_ptr;
oplmsu_link_high_primsg(&lpath->first_lpri_hi,
&lpath->last_lpri_hi, mp);
mutex_exit(&oplmsu_uinst->l_lock);
rw_exit(&oplmsu_uinst->lock);
oplmsu_rcmn_high_qenable(lrq);
} else {
(void) putq(lrq, mp);
}
return (SUCCESS);
}
int
oplmsu_lrsrv(queue_t *lrq)
{
mblk_t *mp;
boolean_t aborted;
int rval;
if ((lrq == NULL) || (lrq->q_ptr == NULL)) {
return (FAILURE);
}
while (mp = getq(lrq)) {
if (mp->b_datap->db_type >= QPCTL) {
cmn_err(CE_WARN, "oplmsu: lr-srv: "
"Invalid db_type => %x", mp->b_datap->db_type);
}
switch (mp->b_datap->db_type) {
case M_DATA :
aborted = B_FALSE;
rw_enter(&oplmsu_uinst->lock, RW_READER);
if ((abort_enable == KIOCABORTALTERNATE) &&
(RD(oplmsu_uinst->lower_queue) == lrq)) {
uchar_t *rx_char = mp->b_rptr;
lpath_t *lpath;
mutex_enter(&oplmsu_uinst->l_lock);
lpath = lrq->q_ptr;
while (rx_char != mp->b_wptr) {
if (*rx_char == *lpath->abt_char) {
lpath->abt_char++;
if (*lpath->abt_char == '\0') {
abort_sequence_enter((char *)
NULL);
lpath->abt_char
= oplmsu_uinst->abts;
aborted = B_TRUE;
break;
}
} else {
lpath->abt_char = (*rx_char ==
*oplmsu_uinst->abts) ?
oplmsu_uinst->abts + 1 :
oplmsu_uinst->abts;
}
rx_char++;
}
mutex_exit(&oplmsu_uinst->l_lock);
}
rw_exit(&oplmsu_uinst->lock);
if (aborted) {
freemsg(mp);
continue;
}
if ((*(mp->b_rptr) == MSU_XON) ||
(((mp->b_wptr - mp->b_rptr) == 2) &&
((*(mp->b_rptr) == MSU_XOFF) &&
(*(mp->b_rptr + 1) == MSU_XON)))) {
if (oplmsu_lrdata_xoffxon(lrq, mp) == FAILURE) {
return (SUCCESS);
}
} else {
rw_enter(&oplmsu_uinst->lock, RW_READER);
rval =
oplmsu_rcmn_through_hndl(lrq, mp, MSU_NORM);
rw_exit(&oplmsu_uinst->lock);
if (rval == FAILURE) {
return (SUCCESS);
}
}
break;
case M_BREAK :
if ((mp->b_wptr - mp->b_rptr) == 0 && msgdsize(mp)
== 0) {
rw_enter(&oplmsu_uinst->lock, RW_READER);
if ((abort_enable != KIOCABORTALTERNATE) &&
(RD(oplmsu_uinst->lower_queue) == lrq)) {
abort_sequence_enter((char *)NULL);
}
rw_exit(&oplmsu_uinst->lock);
freemsg(mp);
break;
}
default :
rw_enter(&oplmsu_uinst->lock, RW_READER);
(void) oplmsu_rcmn_through_hndl(lrq, mp, MSU_NORM);
rw_exit(&oplmsu_uinst->lock);
break;
}
}
return (SUCCESS);
}
int
oplmsu_ursrv(queue_t *urq)
{
mblk_t *mp;
queue_t *dst_queue;
lpath_t *lpath;
ctrl_t *ctrl;
int res_chk = 0;
rw_enter(&oplmsu_uinst->lock, RW_READER);
while (mp = getq(urq)) {
if (mp->b_datap->db_type >= QPCTL) {
if ((mp->b_datap->db_type == M_IOCACK) ||
(mp->b_datap->db_type == M_IOCNAK)) {
res_chk = 1;
}
rw_exit(&oplmsu_uinst->lock);
OPLMSU_TRACE(RD(urq), mp, MSU_TRC_UO);
putnext(RD(urq), mp);
rw_enter(&oplmsu_uinst->lock, RW_READER);
mutex_enter(&oplmsu_uinst->l_lock);
lpath = oplmsu_uinst->first_lpath;
while (lpath) {
qenable(RD(lpath->lower_queue));
lpath = lpath->l_next;
}
mutex_exit(&oplmsu_uinst->l_lock);
if (res_chk == 1) {
mutex_enter(&oplmsu_uinst->c_lock);
ctrl = (ctrl_t *)urq->q_ptr;
if (ctrl != NULL) {
if (ctrl->wait_queue != NULL) {
qenable(WR(ctrl->wait_queue));
ctrl->wait_queue = NULL;
}
}
mutex_exit(&oplmsu_uinst->c_lock);
res_chk = 0;
}
continue;
}
dst_queue = RD(urq);
if (canputnext(dst_queue)) {
rw_exit(&oplmsu_uinst->lock);
OPLMSU_TRACE(dst_queue, mp, MSU_TRC_UO);
putnext(dst_queue, mp);
rw_enter(&oplmsu_uinst->lock, RW_READER);
} else {
(void) putbq(urq, mp);
break;
}
}
mutex_enter(&oplmsu_uinst->c_lock);
ctrl = urq->q_ptr;
if (ctrl->lrq_flag != 0) {
qenable(ctrl->lrq_queue);
ctrl->lrq_flag = 0;
ctrl->lrq_queue = NULL;
}
mutex_exit(&oplmsu_uinst->c_lock);
rw_exit(&oplmsu_uinst->lock);
return (SUCCESS);
}
int
oplmsu_open_msu(dev_info_t *dip, ldi_ident_t *lip, ldi_handle_t *lhp)
{
dev_t devt;
int rval;
rval = ldi_ident_from_dip(dip, lip);
if (rval != 0) {
cmn_err(CE_WARN, "oplmsu: open-msu: "
"ldi_ident_from_dip failed. errno = %d", rval);
return (rval);
}
devt = makedevice(ddi_driver_major(dip), META_NODE_MASK);
rval =
ldi_open_by_dev(&devt, OTYP_CHR, (FREAD|FWRITE), kcred, lhp, *lip);
if (rval != 0) {
cmn_err(CE_WARN, "oplmsu: open-msu: "
"ldi_open_by_dev failed. errno = %d", rval);
ldi_ident_release(*lip);
}
return (rval);
}
int
oplmsu_plink_serial(dev_info_t *dip, ldi_handle_t msu_lh, int *id)
{
ldi_ident_t li = NULL;
ldi_handle_t lh = NULL;
int param;
int rval;
char pathname[MSU_PATHNAME_SIZE];
char wrkbuf[MSU_PATHNAME_SIZE];
(void) ddi_pathname(dip, wrkbuf);
*(wrkbuf + strlen(wrkbuf)) = '\0';
(void) sprintf(pathname, "/devices%s:%c", wrkbuf,
'a'+ ddi_get_instance(dip));
rval = ldi_ident_from_dip(dip, &li);
if (rval != 0) {
cmn_err(CE_WARN, "oplmsu: plink-serial: "
"%s ldi_ident_from_dip failed. errno = %d", pathname, rval);
return (rval);
}
rval = ldi_open_by_name(pathname, (FREAD|FWRITE|FEXCL), kcred, &lh, li);
if (rval != 0) {
cmn_err(CE_WARN, "oplmsu: plink-serial: "
"%s open failed. errno = %d", pathname, rval);
ldi_ident_release(li);
return (rval);
}
param = 0;
while ((ldi_ioctl(lh, I_POP, (intptr_t)0, FKIOCTL, kcred, ¶m))
== 0) {
continue;
}
param = 0;
rval = ldi_ioctl(msu_lh, I_PLINK, (intptr_t)lh, FKIOCTL, kcred, ¶m);
if (rval != 0) {
cmn_err(CE_WARN, "oplmsu: plink-serial: "
"%s ioctl(I_PLINK) failed. errno = %d", pathname, rval);
}
(void) ldi_close(lh, (FREAD|FWRITE|FEXCL), kcred);
ldi_ident_release(li);
*id = param;
return (rval);
}
int
oplmsu_set_lpathnum(int lnk_id, int instance)
{
lpath_t *lpath;
int rval = SUCCESS;
rw_enter(&oplmsu_uinst->lock, RW_READER);
mutex_enter(&oplmsu_uinst->l_lock);
lpath = oplmsu_uinst->first_lpath;
while (lpath) {
if ((lpath->path_no == UNDEFINED) &&
(lpath->link_id == lnk_id)) {
lpath->path_no = instance;
lpath->src_upath = NULL;
lpath->status = MSU_SETID_NU;
break;
}
lpath = lpath->l_next;
}
mutex_exit(&oplmsu_uinst->l_lock);
rw_exit(&oplmsu_uinst->lock);
if (lpath == NULL) {
rval = EINVAL;
}
return (rval);
}
int
oplmsu_dr_attach(dev_info_t *dip)
{
ldi_ident_t msu_li = NULL;
ldi_handle_t msu_lh = NULL;
upath_t *upath;
int len;
int instance;
int lnk_id = 0;
int param = 0;
int rval;
instance = ddi_get_instance(dip);
rw_enter(&oplmsu_uinst->lock, RW_WRITER);
mutex_enter(&oplmsu_uinst->u_lock);
oplmsu_uinst->path_num = oplmsu_get_pathnum();
upath = oplmsu_uinst->first_upath;
while (upath) {
if (instance == upath->path_no) {
break;
}
upath = upath->u_next;
}
mutex_exit(&oplmsu_uinst->u_lock);
rw_exit(&oplmsu_uinst->lock);
if (upath != NULL) {
cmn_err(CE_WARN, "oplmsu: attach(dr): "
"Instance %d already exist", instance);
return (EINVAL);
}
rval = oplmsu_open_msu(oplmsu_uinst->msu_dip, &msu_li, &msu_lh);
if (rval != 0) {
cmn_err(CE_WARN, "oplmsu: attach(dr): "
"msu open failed. errno = %d", rval);
return (rval);
}
rval = oplmsu_plink_serial(dip, msu_lh, &lnk_id);
if (rval != 0) {
cmn_err(CE_WARN, "oplmsu: attach(dr): "
"i_plink failed. errno = %d", rval);
(void) ldi_close(msu_lh, (FREAD|FWRITE), kcred);
ldi_ident_release(msu_li);
return (rval);
}
rval = oplmsu_set_lpathnum(lnk_id, instance);
if (rval != 0) {
cmn_err(CE_WARN, "oplmsu: attach(dr): "
"Link id %d is not found", lnk_id);
(void) ldi_ioctl(msu_lh, I_PUNLINK, (intptr_t)lnk_id, FKIOCTL,
kcred, ¶m);
(void) ldi_close(msu_lh, (FREAD|FWRITE), kcred);
ldi_ident_release(msu_li);
return (rval);
}
rval = oplmsu_config_add(dip);
if (rval != 0) {
cmn_err(CE_WARN, "oplmsu: attach(dr): "
"Failed to add the path. errno = %d", rval);
(void) ldi_ioctl(msu_lh, I_PUNLINK, (intptr_t)lnk_id, FKIOCTL,
kcred, ¶m);
(void) ldi_close(msu_lh, (FREAD|FWRITE), kcred);
ldi_ident_release(msu_li);
return (rval);
}
rval = oplmsu_config_start(instance);
if (rval != 0) {
struct msu_path *mpath;
struct msu_dev *mdev;
cmn_err(CE_WARN, "oplmsu: attach(dr): "
"Failed to start the path. errno = %d", rval);
len = sizeof (struct msu_path) + sizeof (struct msu_dev);
mpath = (struct msu_path *)kmem_zalloc((size_t)len, KM_SLEEP);
mpath->num = 1;
mdev = (struct msu_dev *)(mpath + 1);
mdev->dip = dip;
if ((oplmsu_config_del(mpath)) == 0) {
(void) ldi_ioctl(msu_lh, I_PUNLINK, (intptr_t)lnk_id,
FKIOCTL, kcred, ¶m);
}
kmem_free(mpath, (size_t)len);
}
(void) ldi_close(msu_lh, (FREAD|FWRITE), kcred);
ldi_ident_release(msu_li);
return (rval);
}
int
oplmsu_dr_detach(dev_info_t *dip)
{
ldi_ident_t msu_li = NULL;
ldi_handle_t msu_lh = NULL;
struct msu_path *mpath;
struct msu_dev *mdev;
upath_t *upath;
lpath_t *lpath;
int len;
int instance;
int count = 0;
int param = 0;
int status;
int rval;
instance = ddi_get_instance(dip);
rw_enter(&oplmsu_uinst->lock, RW_WRITER);
mutex_enter(&oplmsu_uinst->u_lock);
oplmsu_uinst->path_num = oplmsu_get_pathnum();
rval = FAILURE;
upath = oplmsu_uinst->first_upath;
while (upath) {
if (instance == upath->path_no) {
status = upath->status;
rval = SUCCESS;
}
upath = upath->u_next;
count += 1;
}
mutex_exit(&oplmsu_uinst->u_lock);
rw_exit(&oplmsu_uinst->lock);
if (rval == FAILURE) {
if (count <= 1) {
cmn_err(CE_WARN, "oplmsu: detach(dr): "
"Instance %d is last path", instance);
} else {
cmn_err(CE_WARN, "oplmsu: detach(dr): "
"Instance %d doesn't find", instance);
}
return (EINVAL);
}
if ((status == MSU_PSTAT_ACTIVE) || (status == MSU_PSTAT_STANDBY)) {
rval = oplmsu_config_stop(instance);
if (rval != 0) {
cmn_err(CE_WARN, "oplmsu: detach(dr): "
"Failed to stop the path. errno = %d", rval);
return (rval);
}
}
rval = oplmsu_config_disc(instance);
if (rval != 0) {
cmn_err(CE_WARN, "oplmsu: detach(dr): "
"Failed to disconnect the path. errno = %d", rval);
return (rval);
}
rw_enter(&oplmsu_uinst->lock, RW_READER);
mutex_enter(&oplmsu_uinst->l_lock);
lpath = oplmsu_uinst->first_lpath;
while (lpath) {
if (lpath->path_no == instance) {
break;
}
lpath = lpath->l_next;
}
mutex_exit(&oplmsu_uinst->l_lock);
rw_exit(&oplmsu_uinst->lock);
if (lpath == NULL) {
cmn_err(CE_WARN, "oplmsu: detach(dr): Can not find link ID");
return (EINVAL);
}
rval = oplmsu_open_msu(oplmsu_uinst->msu_dip, &msu_li, &msu_lh);
if (rval != 0) {
cmn_err(CE_WARN, "oplmsu: detach(dr): "
"msu open failed. errno = %d", rval);
return (rval);
}
rval = ldi_ioctl(msu_lh, I_PUNLINK, (intptr_t)lpath->link_id, FKIOCTL,
kcred, ¶m);
if (rval != 0) {
cmn_err(CE_WARN, "oplmsu: detach(dr): "
"ioctl(I_PUNLINK) failed. errno = %d", rval);
(void) ldi_close(msu_lh, (FREAD|FWRITE), kcred);
ldi_ident_release(msu_li);
return (rval);
}
(void) ldi_close(msu_lh, (FREAD|FWRITE), kcred);
ldi_ident_release(msu_li);
len = sizeof (struct msu_path) + sizeof (struct msu_dev);
mpath = (struct msu_path *)kmem_zalloc((size_t)len, KM_SLEEP);
mpath->num = 1;
mdev = (struct msu_dev *)(mpath + 1);
mdev->dip = dip;
rval = oplmsu_config_del(mpath);
if (rval != 0) {
cmn_err(CE_WARN, "oplmsu: detach(dr): "
"Failed to delete the path. errno = %d", rval);
}
kmem_free(mpath, (size_t)len);
return (rval);
}
#define EBUS_PATH "ebus@1"
#define SERIAL_PATH "serial@14,400000"
#define EBUS_SERIAL_PATH ("/" EBUS_PATH "/" SERIAL_PATH)
dev_info_t *
oplmsu_find_ser_dip(dev_info_t *cmuch_dip)
{
dev_info_t *ebus_dip;
dev_info_t *ser_dip = NULL;
ndi_devi_enter(cmuch_dip);
ebus_dip = ndi_devi_findchild(cmuch_dip, EBUS_PATH);
DBG_PRINT((CE_NOTE, "oplmsu: find-serial-dip: "
"ebus_dip = %p", (void *)ebus_dip));
if (ebus_dip != NULL) {
ndi_devi_enter(ebus_dip);
ser_dip = ndi_devi_findchild(ebus_dip, SERIAL_PATH);
DBG_PRINT((CE_NOTE, "oplmsu: find-serial-dip: "
"ser_dip = %p", (void *)ser_dip));
ndi_devi_exit(ebus_dip);
}
ndi_devi_exit(cmuch_dip);
return (ser_dip);
}
int
oplmsu_find_serial(ser_devl_t **ser_dl)
{
dev_info_t *root_dip;
dev_info_t *cmuch_dip;
dev_info_t *dip;
ser_devl_t *wrk_ser_dl;
int count = 0;
char pathname[MSU_PATHNAME_SIZE];
dev_t devt;
char *namep;
root_dip = ddi_root_node();
ndi_devi_enter(root_dip);
cmuch_dip = ddi_get_child(root_dip);
while (cmuch_dip != NULL) {
namep = ddi_binding_name(cmuch_dip);
if (namep == NULL) {
cmuch_dip = ddi_get_next_sibling(cmuch_dip);
continue;
}
DBG_PRINT((CE_NOTE, "oplmsu: find-serial: name => %s", namep));
if ((strcmp(namep, MSU_CMUCH_FF) != 0) &&
(strcmp(namep, MSU_CMUCH_DC) != 0)) {
#ifdef DEBUG
if (strcmp(namep, MSU_CMUCH_DBG) != 0) {
cmuch_dip = ddi_get_next_sibling(cmuch_dip);
continue;
}
#else
cmuch_dip = ddi_get_next_sibling(cmuch_dip);
continue;
#endif
}
(void) ndi_devi_online(cmuch_dip, 0);
(void) ddi_pathname(cmuch_dip, pathname);
DBG_PRINT((CE_NOTE,
"oplmsu: find-serial: cmu-ch path => %s", pathname));
(void) strcat(pathname, EBUS_SERIAL_PATH);
devt = ddi_pathname_to_dev_t(pathname);
DBG_PRINT((CE_NOTE, "oplmsu: find-serial: serial device "
"dev_t = %lx", devt));
if ((devt != NODEV) &&
((dip = oplmsu_find_ser_dip(cmuch_dip)) != NULL)) {
wrk_ser_dl = (ser_devl_t *)
kmem_zalloc(sizeof (ser_devl_t), KM_SLEEP);
wrk_ser_dl->dip = dip;
count += 1;
if (*ser_dl != NULL) {
wrk_ser_dl->next = *ser_dl;
}
*ser_dl = wrk_ser_dl;
}
cmuch_dip = ddi_get_next_sibling(cmuch_dip);
}
ndi_devi_exit(root_dip);
return (count);
}
void
oplmsu_conf_stream(uinst_t *msu_uinst)
{
ldi_ident_t msu_li = NULL;
ldi_handle_t msu_lh = NULL;
struct msu_path *mpath;
struct msu_dev *mdev;
ser_devl_t *ser_dl = NULL, *next_ser_dl;
int *plink_id;
int size;
int i;
int param;
int connected = 0;
int devcnt = 0;
int rval;
DBG_PRINT((CE_NOTE,
"oplmsu: conf-stream: stream configuration start!"));
devcnt = oplmsu_find_serial(&ser_dl);
if ((devcnt == 0) || (ser_dl == NULL)) {
cmn_err(CE_WARN, "oplmsu: conf-stream: "
"Discovered serial device = %d", devcnt);
return;
}
rval = oplmsu_open_msu(msu_uinst->msu_dip, &msu_li, &msu_lh);
if (rval != 0) {
cmn_err(CE_WARN, "oplmsu: conf-stream: "
"msu open failed. errno = %d", rval);
return;
}
size = (sizeof (struct msu_path) + (sizeof (struct msu_dev) * devcnt));
mpath = (struct msu_path *)kmem_zalloc((size_t)size, KM_SLEEP);
plink_id = (int *)kmem_zalloc((sizeof (int) * devcnt), KM_SLEEP);
mdev = (struct msu_dev *)(mpath + 1);
for (i = 0; i < devcnt; i++) {
rval = oplmsu_plink_serial(ser_dl->dip, msu_lh, &plink_id[i]);
if (rval != 0) {
cmn_err(CE_WARN, "oplmsu: conf-stream: "
"i_plink failed. errno = %d", rval);
next_ser_dl = ser_dl->next;
kmem_free(ser_dl, sizeof (ser_devl_t));
ser_dl = next_ser_dl;
continue;
}
rval = oplmsu_set_lpathnum(plink_id[i],
ddi_get_instance(ser_dl->dip));
if (rval != 0) {
cmn_err(CE_WARN, "oplmsu: conf-stream: "
"Link id %d is not found", plink_id[i]);
(void) ldi_ioctl(msu_lh, I_PUNLINK,
(intptr_t)plink_id[i], FKIOCTL, kcred, ¶m);
next_ser_dl = ser_dl->next;
kmem_free(ser_dl, sizeof (ser_devl_t));
ser_dl = next_ser_dl;
continue;
}
mdev->dip = ser_dl->dip;
next_ser_dl = ser_dl->next;
kmem_free(ser_dl, sizeof (ser_devl_t));
ser_dl = next_ser_dl;
mdev++;
connected++;
}
if (connected == 0) {
cmn_err(CE_WARN, "oplmsu: conf-stream: "
"Connected paths = %d", connected);
(void) ldi_close(msu_lh, (FREAD|FWRITE), kcred);
ldi_ident_release(msu_li);
kmem_free(plink_id, (sizeof (int) * devcnt));
kmem_free(mpath, size);
return;
}
mpath->num = connected;
rval = oplmsu_config_new(mpath);
if (rval != 0) {
cmn_err(CE_WARN, "oplmsu: conf-stream: "
"Failed to create all paths. errno = %d", rval);
oplmsu_unlinks(msu_lh, plink_id, devcnt);
(void) ldi_close(msu_lh, (FREAD|FWRITE), kcred);
ldi_ident_release(msu_li);
kmem_free(plink_id, (sizeof (int) * devcnt));
kmem_free(mpath, size);
return;
}
rval = oplmsu_config_start(MSU_PATH_ALL);
if (rval != 0) {
cmn_err(CE_WARN, "oplmsu: conf-stream: "
"Failed to start all paths. errno = %d", rval);
rval = oplmsu_config_del(mpath);
if (rval == 0) {
oplmsu_unlinks(msu_lh, plink_id, devcnt);
}
}
(void) ldi_close(msu_lh, (FREAD|FWRITE), kcred);
ldi_ident_release(msu_li);
kmem_free(plink_id, (sizeof (int) * devcnt));
kmem_free(mpath, size);
DBG_PRINT((CE_NOTE, "oplmsu: conf-stream: stream configuration end!"));
}
void
oplmsu_unlinks(ldi_handle_t msu_lh, int *plink_id, int devcnt)
{
int i;
int param = 0;
for (i = 0; i < devcnt; i++) {
if (plink_id[i] == 0) {
continue;
}
(void) ldi_ioctl(msu_lh, I_PUNLINK, (intptr_t)plink_id[i],
FKIOCTL, kcred, ¶m);
}
}
void
oplmsu_setup(uinst_t *msu_uinst)
{
DBG_PRINT((CE_NOTE, "oplmsu: setup: Background thread start!"));
mutex_enter(&oplmsu_bthrd_excl);
if (oplmsu_conf_st == MSU_CONFIGURING) {
mutex_exit(&oplmsu_bthrd_excl);
oplmsu_conf_stream(msu_uinst);
mutex_enter(&oplmsu_bthrd_excl);
oplmsu_conf_st = MSU_CONFIGURED;
cv_broadcast(&oplmsu_conf_cv);
}
if (oplmsu_bthrd_id != NULL) {
oplmsu_bthrd_id = NULL;
}
mutex_exit(&oplmsu_bthrd_excl);
DBG_PRINT((CE_NOTE, "oplmsu: setup: Background thread end!"));
thread_exit();
}
int
oplmsu_create_upath(dev_info_t *dip)
{
upath_t *upath;
lpath_t *lpath;
dev_info_t *cmuch_dip;
int instance;
int lsb;
cmuch_dip = ddi_get_parent(ddi_get_parent(dip));
lsb = ddi_prop_get_int(DDI_DEV_T_ANY, cmuch_dip, 0, MSU_BOARD_PROP,
FAILURE);
if (lsb == FAILURE) {
return (lsb);
}
instance = ddi_get_instance(dip);
mutex_enter(&oplmsu_uinst->l_lock);
lpath = oplmsu_uinst->first_lpath;
while (lpath) {
if (lpath->path_no == instance) {
break;
}
lpath = lpath->l_next;
}
if (lpath == NULL) {
mutex_exit(&oplmsu_uinst->l_lock);
return (ENODEV);
}
upath = (upath_t *)kmem_zalloc(sizeof (upath_t), KM_SLEEP);
upath->path_no = instance;
upath->lpath = lpath;
upath->ser_devcb.dip = dip;
upath->ser_devcb.lsb = lsb;
oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_STOP, MSU_PSTAT_EMPTY,
MSU_STOP);
lpath->src_upath = NULL;
lpath->status = MSU_EXT_NOTUSED;
mutex_exit(&oplmsu_uinst->l_lock);
oplmsu_link_upath(upath);
return (SUCCESS);
}
int
oplmsu_config_new(struct msu_path *mpath)
{
struct msu_dev *mdev;
int i;
int rval = SUCCESS;
DBG_PRINT((CE_NOTE, "oplmsu: conf-new: config_new() called"));
ASSERT(mpath);
if (mpath->num == 0) {
cmn_err(CE_WARN, "oplmsu: conf-new: "
"Number of paths = %d", mpath->num);
return (EINVAL);
}
rw_enter(&oplmsu_uinst->lock, RW_WRITER);
mutex_enter(&oplmsu_uinst->l_lock);
rval = oplmsu_check_lpath_usable();
mutex_exit(&oplmsu_uinst->l_lock);
if (rval == BUSY) {
rw_exit(&oplmsu_uinst->lock);
cmn_err(CE_WARN, "oplmsu: conf-new: "
"Other processing is using this device");
return (EBUSY);
}
mutex_enter(&oplmsu_uinst->u_lock);
if ((oplmsu_uinst->first_upath != NULL) ||
(oplmsu_uinst->last_upath != NULL)) {
mutex_exit(&oplmsu_uinst->u_lock);
rw_exit(&oplmsu_uinst->lock);
cmn_err(CE_WARN, "oplmsu: conf-new: upath_t already exist");
return (EINVAL);
}
if (oplmsu_uinst->path_num != UNDEFINED) {
mutex_exit(&oplmsu_uinst->u_lock);
rw_exit(&oplmsu_uinst->lock);
cmn_err(CE_WARN, "oplmsu: conf-new: "
"conf-new processing has already been completed");
return (EINVAL);
}
mdev = (struct msu_dev *)(mpath + 1);
for (i = 0; i < mpath->num; i++) {
rval = oplmsu_create_upath(mdev->dip);
if (rval != SUCCESS) {
oplmsu_delete_upath_info();
mutex_exit(&oplmsu_uinst->u_lock);
rw_exit(&oplmsu_uinst->lock);
cmn_err(CE_WARN, "oplmsu: conf-new: "
"Failed to create upath %d", rval);
return (rval);
}
mdev++;
}
oplmsu_uinst->inst_status = oplmsu_get_inst_status();
oplmsu_uinst->path_num = mpath->num;
oplmsu_uinst->lower_queue = NULL;
mutex_exit(&oplmsu_uinst->u_lock);
rw_exit(&oplmsu_uinst->lock);
return (SUCCESS);
}
int
oplmsu_config_add(dev_info_t *dip)
{
upath_t *upath;
int instance;
int rval = SUCCESS;
DBG_PRINT((CE_NOTE, "oplmsu: conf-add: config_add() called"));
ASSERT(dip);
instance = ddi_get_instance(dip);
rw_enter(&oplmsu_uinst->lock, RW_WRITER);
if (oplmsu_uinst->path_num == UNDEFINED) {
rw_exit(&oplmsu_uinst->lock);
cmn_err(CE_WARN, "oplmsu: conf-add: "
"conf-new processing has not been completed yet");
return (EINVAL);
}
mutex_enter(&oplmsu_uinst->u_lock);
upath = oplmsu_search_upath_info(instance);
if (upath != NULL) {
mutex_exit(&oplmsu_uinst->u_lock);
rw_exit(&oplmsu_uinst->lock);
cmn_err(CE_WARN, "oplmsu: conf-add: "
"Proper upath_t doesn't find");
return (EINVAL);
}
rval = oplmsu_create_upath(dip);
if (rval != SUCCESS) {
mutex_exit(&oplmsu_uinst->u_lock);
rw_exit(&oplmsu_uinst->lock);
cmn_err(CE_WARN, "oplmsu: conf-add: "
"Failed to create upath %d", rval);
return (rval);
}
oplmsu_uinst->inst_status = oplmsu_get_inst_status();
oplmsu_uinst->path_num = oplmsu_get_pathnum();
mutex_exit(&oplmsu_uinst->u_lock);
rw_exit(&oplmsu_uinst->lock);
return (SUCCESS);
}
int
oplmsu_config_del(struct msu_path *mpath)
{
struct msu_dev *mdev;
upath_t *upath;
lpath_t *lpath;
int rval = SUCCESS;
int use_flag;
int i;
DBG_PRINT((CE_NOTE, "oplmsu: conf-del: config_del() called"));
ASSERT(mpath);
mdev = (struct msu_dev *)(mpath + 1);
rw_enter(&oplmsu_uinst->lock, RW_WRITER);
mutex_enter(&oplmsu_uinst->u_lock);
for (i = 0; i < mpath->num; i++) {
upath = oplmsu_search_upath_info(ddi_get_instance(mdev->dip));
if (upath == NULL) {
cmn_err(CE_WARN, "oplmsu: conf-del: "
"Proper upath_t doesn't find");
rval = ENODEV;
mdev++;
continue;
}
lpath = upath->lpath;
if (lpath == NULL) {
if ((upath->traditional_status == MSU_WSTP_ACK) ||
(upath->traditional_status == MSU_WSTR_ACK) ||
(upath->traditional_status == MSU_WPTH_CHG) ||
(upath->traditional_status == MSU_WTCS_ACK) ||
(upath->traditional_status == MSU_WTMS_ACK) ||
(upath->traditional_status == MSU_WPPS_ACK) ||
(upath->traditional_status == MSU_WWSZ_ACK) ||
(upath->traditional_status == MSU_WCAR_ACK)) {
cmn_err(CE_WARN, "oplmsu: conf-del: "
"Other processing is using this device");
rval = EBUSY;
mdev++;
continue;
}
if ((upath->status != MSU_PSTAT_DISCON) ||
(upath->traditional_status != MSU_DISCON)) {
cmn_err(CE_WARN, "oplmsu: conf-del: "
"Status of path is improper");
rval = EINVAL;
mdev++;
continue;
}
} else {
mutex_enter(&oplmsu_uinst->l_lock);
use_flag = oplmsu_set_ioctl_path(lpath, NULL, NULL);
if (use_flag == BUSY) {
mutex_exit(&oplmsu_uinst->l_lock);
cmn_err(CE_WARN, "oplmsu: conf-del: "
"Other processing is using lower path");
rval = EBUSY;
mdev++;
continue;
}
if (((upath->status != MSU_PSTAT_STOP) ||
(upath->traditional_status != MSU_STOP)) &&
((upath->status != MSU_PSTAT_FAIL) ||
(upath->traditional_status != MSU_FAIL))) {
oplmsu_clear_ioctl_path(lpath);
mutex_exit(&oplmsu_uinst->l_lock);
cmn_err(CE_WARN, "oplmsu: conf-del: "
"Status of path isn't 'Offline:stop/fail'");
rval = EINVAL;
mdev++;
continue;
}
lpath->src_upath = NULL;
lpath->status = MSU_SETID_NU;
oplmsu_clear_ioctl_path(lpath);
mutex_exit(&oplmsu_uinst->l_lock);
}
oplmsu_unlink_upath(upath);
kmem_free(upath, sizeof (upath_t));
mdev++;
}
oplmsu_uinst->inst_status = oplmsu_get_inst_status();
oplmsu_uinst->path_num = oplmsu_get_pathnum();
mutex_exit(&oplmsu_uinst->u_lock);
rw_exit(&oplmsu_uinst->lock);
return (rval);
}
int
oplmsu_config_stop(int pathnum)
{
upath_t *upath, *altn_upath;
lpath_t *lpath, *altn_lpath;
queue_t *stp_queue = NULL;
queue_t *dst_queue = NULL;
mblk_t *nmp = NULL, *fmp = NULL;
ctrl_t *ctrl;
int term_ioctl, term_stat;
int use_flag;
DBG_PRINT((CE_NOTE,
"oplmsu: conf-stop: config_stop(%d) called", pathnum));
if (pathnum == MSU_PATH_ALL) {
cmn_err(CE_WARN, "oplmsu: conf-stop: "
"All path can't be transferred to the status of "
"'Offline:stop'");
return (EINVAL);
}
rw_enter(&oplmsu_uinst->lock, RW_WRITER);
mutex_enter(&oplmsu_uinst->u_lock);
upath = oplmsu_search_upath_info(pathnum);
if (upath == NULL) {
mutex_exit(&oplmsu_uinst->u_lock);
rw_exit(&oplmsu_uinst->lock);
cmn_err(CE_WARN, "oplmsu: conf-stop: "
"Proper upath_t doesn't find");
return (ENODEV);
}
lpath = upath->lpath;
if (lpath == NULL) {
mutex_exit(&oplmsu_uinst->u_lock);
rw_exit(&oplmsu_uinst->lock);
cmn_err(CE_WARN, "oplmsu: conf-stop: "
"Proper lpath_t doesn't exist");
return (ENODEV);
}
mutex_enter(&oplmsu_uinst->l_lock);
use_flag = oplmsu_set_ioctl_path(lpath, NULL, NULL);
if (use_flag == BUSY) {
mutex_exit(&oplmsu_uinst->l_lock);
mutex_exit(&oplmsu_uinst->u_lock);
rw_exit(&oplmsu_uinst->lock);
cmn_err(CE_WARN, "oplmsu: conf-stop: "
"Other processing is using lower path");
return (EBUSY);
}
if (upath->status == MSU_PSTAT_FAIL) {
oplmsu_clear_ioctl_path(lpath);
mutex_exit(&oplmsu_uinst->l_lock);
mutex_exit(&oplmsu_uinst->u_lock);
rw_exit(&oplmsu_uinst->lock);
return (EIO);
} else if ((upath->status == MSU_PSTAT_STOP) &&
(upath->traditional_status == MSU_STOP)) {
oplmsu_clear_ioctl_path(lpath);
mutex_exit(&oplmsu_uinst->l_lock);
mutex_exit(&oplmsu_uinst->u_lock);
rw_exit(&oplmsu_uinst->lock);
return (SUCCESS);
} else if ((upath->status == MSU_PSTAT_STANDBY) &&
(upath->traditional_status == MSU_STANDBY)) {
oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_STOP,
upath->status, MSU_STOP);
oplmsu_clear_ioctl_path(lpath);
lpath->src_upath = NULL;
lpath->status = MSU_EXT_NOTUSED;
oplmsu_uinst->inst_status = oplmsu_get_inst_status();
mutex_exit(&oplmsu_uinst->l_lock);
mutex_exit(&oplmsu_uinst->u_lock);
rw_exit(&oplmsu_uinst->lock);
return (SUCCESS);
} else if ((upath->status == MSU_PSTAT_ACTIVE) &&
(upath->traditional_status == MSU_ACTIVE)) {
altn_upath = oplmsu_search_standby();
if (altn_upath == NULL) {
DBG_PRINT((CE_NOTE, "oplmsu: conf-stop: "
"Alternate upper path doesn't find"));
oplmsu_clear_ioctl_path(lpath);
mutex_exit(&oplmsu_uinst->l_lock);
mutex_exit(&oplmsu_uinst->u_lock);
rw_exit(&oplmsu_uinst->lock);
return (EINVAL);
}
if ((fmp = allocb(sizeof (char), BPRI_LO)) == NULL) {
oplmsu_clear_ioctl_path(lpath);
mutex_exit(&oplmsu_uinst->l_lock);
mutex_exit(&oplmsu_uinst->u_lock);
rw_exit(&oplmsu_uinst->lock);
return (ENOSR);
}
if (oplmsu_stop_prechg(&nmp, &term_ioctl, &term_stat) !=
SUCCESS) {
oplmsu_clear_ioctl_path(lpath);
mutex_exit(&oplmsu_uinst->l_lock);
mutex_exit(&oplmsu_uinst->u_lock);
rw_exit(&oplmsu_uinst->lock);
freeb(fmp);
return (ENOSR);
}
altn_lpath = altn_upath->lpath;
use_flag = oplmsu_set_ioctl_path(altn_lpath, NULL, NULL);
if (use_flag == BUSY) {
oplmsu_clear_ioctl_path(lpath);
mutex_exit(&oplmsu_uinst->l_lock);
mutex_exit(&oplmsu_uinst->u_lock);
rw_exit(&oplmsu_uinst->lock);
cmn_err(CE_WARN, "oplmsu: conf-stop: "
"Other processing is using alternate lower path");
freeb(fmp);
freemsg(nmp);
return (EBUSY);
}
dst_queue = WR(altn_lpath->lower_queue);
if (nmp == NULL) {
altn_upath->traditional_status = term_stat;
altn_lpath->src_upath = upath;
altn_lpath->status = MSU_EXT_VOID;
oplmsu_uinst->lower_queue = NULL;
ctrl = oplmsu_uinst->user_ctrl;
if (ctrl != NULL) {
mutex_enter(&oplmsu_uinst->c_lock);
stp_queue = WR(ctrl->queue);
mutex_exit(&oplmsu_uinst->c_lock);
noenable(stp_queue);
oplmsu_queue_flag = 1;
}
oplmsu_cmn_set_mflush(fmp);
(void) putq(dst_queue, fmp);
oplmsu_cmn_set_upath_sts(altn_upath, MSU_PSTAT_ACTIVE,
altn_upath->status, MSU_ACTIVE);
oplmsu_clear_ioctl_path(altn_lpath);
altn_lpath->uinst = oplmsu_uinst;
altn_lpath->src_upath = NULL;
altn_lpath->status = MSU_EXT_NOTUSED;
(void) prom_opl_switch_console(
altn_upath->ser_devcb.lsb);
(void) oplmsu_cmn_put_xoffxon(dst_queue, MSU_XON_4);
oplmsu_cmn_putxoff_standby();
oplmsu_uinst->lower_queue = RD(dst_queue);
ctrl = oplmsu_uinst->user_ctrl;
if (ctrl != NULL) {
queue_t *altn_queue;
mutex_enter(&oplmsu_uinst->c_lock);
altn_queue = WR(ctrl->queue);
mutex_exit(&oplmsu_uinst->c_lock);
enableok(altn_queue);
oplmsu_queue_flag = 0;
mutex_exit(&oplmsu_uinst->l_lock);
mutex_exit(&oplmsu_uinst->u_lock);
oplmsu_wcmn_high_qenable(altn_queue, RW_WRITER);
mutex_enter(&oplmsu_uinst->u_lock);
mutex_enter(&oplmsu_uinst->l_lock);
}
oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_STOP,
upath->status, MSU_STOP);
lpath->uinst = NULL;
lpath->src_upath = NULL;
lpath->status = MSU_EXT_NOTUSED;
oplmsu_clear_ioctl_path(lpath);
oplmsu_uinst->inst_status = oplmsu_get_inst_status();
mutex_exit(&oplmsu_uinst->l_lock);
mutex_exit(&oplmsu_uinst->u_lock);
rw_exit(&oplmsu_uinst->lock);
return (SUCCESS);
}
if (canput(dst_queue)) {
altn_upath->traditional_status = term_stat;
altn_lpath->src_upath = upath;
altn_lpath->status = MSU_EXT_VOID;
upath->traditional_status = MSU_WSTP_ACK;
lpath->uinst = NULL;
oplmsu_uinst->lower_queue = NULL;
ctrl = oplmsu_uinst->user_ctrl;
if (ctrl != NULL) {
mutex_enter(&oplmsu_uinst->c_lock);
stp_queue = WR(ctrl->queue);
mutex_exit(&oplmsu_uinst->c_lock);
noenable(stp_queue);
oplmsu_queue_flag = 1;
}
mutex_exit(&oplmsu_uinst->l_lock);
mutex_exit(&oplmsu_uinst->u_lock);
rw_exit(&oplmsu_uinst->lock);
oplmsu_cmn_set_mflush(fmp);
(void) putq(dst_queue, fmp);
(void) putq(dst_queue, nmp);
mutex_enter(&oplmsu_uinst->l_lock);
lpath->sw_flag = 1;
while (lpath->sw_flag != 0) {
cv_wait(&lpath->sw_cv, &oplmsu_uinst->l_lock);
}
mutex_exit(&oplmsu_uinst->l_lock);
return (SUCCESS);
} else {
oplmsu_clear_ioctl_path(altn_lpath);
oplmsu_clear_ioctl_path(lpath);
mutex_exit(&oplmsu_uinst->l_lock);
mutex_exit(&oplmsu_uinst->u_lock);
rw_exit(&oplmsu_uinst->lock);
freeb(fmp);
freemsg(nmp);
return (FAILURE);
}
} else {
oplmsu_clear_ioctl_path(lpath);
mutex_exit(&oplmsu_uinst->l_lock);
mutex_exit(&oplmsu_uinst->u_lock);
rw_exit(&oplmsu_uinst->lock);
cmn_err(CE_WARN, "oplmsu: conf-stop: "
"Status of path is improper");
return (EINVAL);
}
}
int
oplmsu_config_start(int pathnum)
{
upath_t *upath = NULL;
lpath_t *lpath = NULL;
queue_t *dst_queue, *main_rq = NULL;
int msu_tty_port;
DBG_PRINT((CE_NOTE,
"oplmsu: conf-start: config_start(%d) called", pathnum));
rw_enter(&oplmsu_uinst->lock, RW_WRITER);
mutex_enter(&oplmsu_uinst->u_lock);
if (oplmsu_get_inst_status() == INST_STAT_BUSY) {
mutex_exit(&oplmsu_uinst->u_lock);
rw_exit(&oplmsu_uinst->lock);
return (EBUSY);
}
if (pathnum == MSU_PATH_ALL) {
(void) oplmsu_search_min_stop_path();
}
for (upath = oplmsu_uinst->first_upath; upath; ) {
if ((pathnum != MSU_PATH_ALL) && (upath->path_no != pathnum)) {
upath = upath->u_next;
continue;
}
if (upath->path_no == pathnum) {
lpath = upath->lpath;
if (lpath == NULL) {
mutex_exit(&oplmsu_uinst->u_lock);
rw_exit(&oplmsu_uinst->lock);
cmn_err(CE_WARN, "oplmsu: conf-start: "
"Proper lpath_t doesn't exist");
return (EINVAL);
}
oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_STANDBY,
upath->status, MSU_STANDBY);
mutex_enter(&oplmsu_uinst->l_lock);
lpath->src_upath = NULL;
lpath->status = MSU_EXT_NOTUSED;
mutex_exit(&oplmsu_uinst->l_lock);
mutex_exit(&oplmsu_uinst->u_lock);
rw_exit(&oplmsu_uinst->lock);
return (SUCCESS);
}
lpath = upath->lpath;
if (lpath == NULL) {
upath = upath->u_next;
DBG_PRINT((CE_WARN, "oplmsu: conf-start: "
"Proper lpath_t doesn't exist"));
continue;
}
msu_tty_port = ddi_prop_get_int(DDI_DEV_T_ANY,
oplmsu_uinst->msu_dip, 0, MSU_TTY_PORT_PROP, -1);
if (upath->ser_devcb.lsb == msu_tty_port) {
(void) prom_opl_switch_console(upath->ser_devcb.lsb);
oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_ACTIVE,
upath->status, MSU_ACTIVE);
mutex_enter(&oplmsu_uinst->l_lock);
main_rq = RD(lpath->lower_queue);
dst_queue = WR(lpath->lower_queue);
lpath->src_upath = NULL;
lpath->status = MSU_EXT_NOTUSED;
lpath->uinst = oplmsu_uinst;
mutex_exit(&oplmsu_uinst->l_lock);
(void) oplmsu_cmn_put_xoffxon(dst_queue, MSU_XON_4);
} else {
oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_STANDBY,
upath->status, MSU_STANDBY);
mutex_enter(&oplmsu_uinst->l_lock);
lpath->src_upath = NULL;
lpath->status = MSU_EXT_NOTUSED;
mutex_exit(&oplmsu_uinst->l_lock);
}
upath = upath->u_next;
}
if (main_rq == NULL) {
upath_t *altn_upath;
lpath_t *altn_lpath;
altn_upath = oplmsu_search_standby();
if (altn_upath) {
oplmsu_cmn_set_upath_sts(altn_upath, MSU_PSTAT_ACTIVE,
altn_upath->status, MSU_ACTIVE);
(void) prom_opl_switch_console(
altn_upath->ser_devcb.lsb);
altn_lpath = altn_upath->lpath;
if (altn_lpath) {
mutex_enter(&oplmsu_uinst->l_lock);
main_rq = RD(altn_lpath->lower_queue);
dst_queue = WR(altn_lpath->lower_queue);
altn_lpath->src_upath = NULL;
altn_lpath->status = MSU_EXT_NOTUSED;
altn_lpath->uinst = oplmsu_uinst;
mutex_exit(&oplmsu_uinst->l_lock);
(void) oplmsu_cmn_put_xoffxon(dst_queue,
MSU_XON_4);
} else {
cmn_err(CE_WARN, "oplmsu: conf-start: "
"Proper alternate lpath_t doesn't exist");
}
} else {
cmn_err(CE_WARN, "oplmsu: conf-start: "
"Proper alternate upath_t doesn't exist");
}
}
mutex_enter(&oplmsu_uinst->l_lock);
oplmsu_cmn_putxoff_standby();
oplmsu_uinst->lower_queue = main_rq;
oplmsu_uinst->inst_status = oplmsu_get_inst_status();
mutex_exit(&oplmsu_uinst->l_lock);
mutex_exit(&oplmsu_uinst->u_lock);
rw_exit(&oplmsu_uinst->lock);
return (SUCCESS);
}
int
oplmsu_config_disc(int pathnum)
{
upath_t *upath;
lpath_t *lpath;
int use_flag;
DBG_PRINT((CE_NOTE,
"oplmsu: conf-disc: config_disc(%d) called", pathnum));
rw_enter(&oplmsu_uinst->lock, RW_READER);
mutex_enter(&oplmsu_uinst->u_lock);
upath = oplmsu_search_upath_info(pathnum);
if (upath == NULL) {
mutex_exit(&oplmsu_uinst->u_lock);
rw_exit(&oplmsu_uinst->lock);
cmn_err(CE_WARN, "oplmsu: conf-disc: "
"Proper upath_t doesn't find");
return (EINVAL);
}
if ((upath->status == MSU_PSTAT_DISCON) ||
(upath->traditional_status == MSU_DISCON)) {
mutex_exit(&oplmsu_uinst->u_lock);
rw_exit(&oplmsu_uinst->lock);
return (SUCCESS);
} else if (((upath->status != MSU_PSTAT_STOP) ||
(upath->traditional_status != MSU_STOP)) &&
((upath->status != MSU_PSTAT_FAIL) ||
(upath->traditional_status != MSU_FAIL))) {
mutex_exit(&oplmsu_uinst->u_lock);
rw_exit(&oplmsu_uinst->lock);
cmn_err(CE_WARN, "oplmsu: conf-disc: "
"Status of path is improper");
return (EINVAL);
}
lpath = upath->lpath;
if (lpath == NULL) {
mutex_exit(&oplmsu_uinst->u_lock);
rw_exit(&oplmsu_uinst->lock);
cmn_err(CE_WARN, "oplmsu: conf-disc: "
"Proper lpath_t doesn't exist");
return (ENODEV);
}
mutex_enter(&oplmsu_uinst->l_lock);
use_flag = oplmsu_set_ioctl_path(lpath, NULL, NULL);
if (use_flag == BUSY) {
mutex_exit(&oplmsu_uinst->l_lock);
mutex_exit(&oplmsu_uinst->u_lock);
rw_exit(&oplmsu_uinst->lock);
cmn_err(CE_WARN, "oplmsu: conf-disc: "
"Other processing is using lower path");
return (EBUSY);
}
upath->status = MSU_PSTAT_STOP;
upath->traditional_status = MSU_SETID;
oplmsu_clear_ioctl_path(lpath);
mutex_exit(&oplmsu_uinst->l_lock);
mutex_exit(&oplmsu_uinst->u_lock);
rw_exit(&oplmsu_uinst->lock);
return (SUCCESS);
}