smp.c revision 4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6
/*
* 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.
*/
/*
* SMP - Serial Management Protocol Device Driver
*
* The SMP driver provides user programs access to SAS Serial Management
* Protocol devices by providing ioctl interface.
*/
/*
* Standard entrypoints
*/
static int smp_probe(dev_info_t *);
/*
* Configuration routines
*/
static int smp_do_attach(dev_info_t *);
static int smp_do_detach(dev_info_t *);
/*
* Command handle routing
*/
/*
*/
static void smp_log(smp_state_t *, int, const char *, ...);
static int smp_retry_recovered = 0; /* retry recovery counter */
static int smp_retry_failed = 0; /* retry failed counter */
static int smp_failed = 0;
static struct cb_ops smp_cb_ops = {
smp_open, /* open */
smp_close, /* close */
nodev, /* strategy */
nodev, /* print */
nodev, /* dump */
nodev, /* read */
nodev, /* write */
smp_ioctl, /* ioctl */
nodev, /* devmap */
nodev, /* mmap */
nodev, /* segmap */
nochpoll, /* poll */
ddi_prop_op, /* cb_prop_op */
0, /* streamtab */
};
static struct dev_ops smp_dev_ops = {
DEVO_REV, /* devo_rev, */
0, /* refcnt */
smp_getinfo, /* info */
nulldev, /* identify */
smp_probe, /* probe */
smp_attach, /* attach */
smp_detach, /* detach */
nodev, /* reset */
&smp_cb_ops, /* driver operations */
(struct bus_ops *)0, /* bus operations */
NULL, /* power */
ddi_quiesce_not_needed, /* quiesce */
};
static void *smp_soft_state = NULL;
};
static struct modlinkage modlinkage = {
};
int
_init(void)
{
int err;
sizeof (smp_state_t), SMP_ESTIMATED_NUM_DEVS)) != 0) {
return (err);
}
}
return (err);
}
int
_fini(void)
{
int err;
}
return (err);
}
int
{
}
/*
* smp_attach()
* attach(9e) entrypoint.
*/
static int
{
int err;
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
err = DDI_SUCCESS;
break;
default:
err = DDI_FAILURE;
break;
}
if (err != DDI_SUCCESS) {
"device unit-address @%s failed",
}
return (err);
}
/*
* smp_do_attach()
* handle the nitty details of attach.
*/
static int
{
int instance;
struct smp_device *smp_devp;
"!smp_do_attach: failed to allocate softstate, "
return (DDI_FAILURE);
}
/*
* For simplicity, the minor number == the instance number
*/
"!smp_do_attach: minor node creation failed, "
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* smp_detach()
* detach(9E) entrypoint
*/
static int
{
int instance;
"!smp_detach: failed, no softstate found (%d), "
"device unit-address @%s",
return (DDI_FAILURE);
}
switch (cmd) {
case DDI_DETACH:
return (smp_do_detach(dip));
case DDI_SUSPEND:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
/*
* smp_do_detach()
* detach the driver, tearing down resources.
*/
static int
{
int instance;
return (DDI_SUCCESS);
}
static int
{
struct smp_device *smpdevp;
return (sas_hba_probe_smp(smpdevp));
}
/*
* smp_getinfo()
* getinfo(9e) entrypoint.
*/
/*ARGSUSED*/
static int
{
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
== NULL)
return (DDI_FAILURE);
error = DDI_SUCCESS;
break;
case DDI_INFO_DEVT2INSTANCE:
error = DDI_SUCCESS;
break;
default:
error = DDI_FAILURE;
}
return (error);
}
/*ARGSUSED*/
static int
{
int instance;
int rv = 0;
== NULL) {
return (ENXIO);
}
} else {
}
} else {
} else {
}
}
return (rv);
}
/*ARGSUSED*/
static int
{
int instance;
int rv = 0;
== NULL) {
return (ENXIO);
}
} else {
}
return (rv);
}
/*ARGSUSED*/
static int
{
int instance, retrycount;
int rval = 0;
#ifdef _MULTI_DATAMODEL
#endif
/* require PRIV_SYS_DEVICES privilege */
cr = ddi_get_cred();
return (EPERM);
}
== NULL) {
return (ENXIO);
}
#ifdef _MULTI_DATAMODEL
case DDI_MODEL_ILP32:
flag)) {
return (EFAULT);
}
break;
case DDI_MODEL_NONE:
flag)) {
return (EFAULT);
}
break;
}
#else /* ! _MULTI_DATAMODEL */
return (EFAULT);
}
#endif /* _MULTI_DATAMODEL */
goto done;
}
/* allocate memory space for smp request and response frame in kernel */
KM_SLEEP);
KM_SLEEP);
/* copy smp request frame to kernel space */
goto done;
}
if (usmp_cmd->usmp_timeout <= 0) {
} else {
}
/* call sas_smp_transport entry and send smp_pkt to HBA driver */
/*
* To improve transport reliability, only allow one command
* outstanding at a time in sas_smp_transport().
*
* NOTE: Some expanders have issues with heavy smp load.
*/
if (smp_single_command) {
}
/* Let the transport know if more retries are possible. */
smp_pkt->pkt_reason = 0;
if (smp_delay_cmd)
if (smp_single_command) {
}
if (rval == DDI_SUCCESS) {
if (retrycount)
rval = 0;
break;
}
switch (smp_pkt->pkt_reason) {
case EAGAIN:
if (retrycount < smp_retry_times) {
if (smp_retry_delay)
continue;
} else {
"!sas_smp_transport failed, pkt_reason %d",
goto copyout;
}
default:
"!sas_smp_transport failed, pkt_reason %d",
goto copyout;
}
}
/* copy out smp response to user process */
}
done:
if ((cmd_flags & SMP_FLAG_XFER) != 0) {
}
if ((cmd_flags & SMP_FLAG_REQBUF) != 0) {
}
if ((cmd_flags & SMP_FLAG_RSPBUF) != 0) {
}
if (rval)
smp_failed++;
return (rval);
}
/*ARGSUSED*/
static int
{
int rval = 0;
switch (cmd) {
case USMPFUNC:
/*
* The response payload is valid only if return value is 0
* or EOVERFLOW.
*/
break;
default:
}
return (rval);
}
static void
{
char buf[256];
} else {
}
}