#include <sys/stat.h>
#include <sys/conf.h>
#include <sys/modctl.h>
#include <sys/ddi.h>
#include <sys/rmc_comm_dp.h>
#include <sys/rmc_comm_dp_boot.h>
#include <sys/rmc_comm_drvintf.h>
#include <sys/cyclic.h>
#include <sys/rmc_comm.h>
#include <sys/machsystm.h>
#include <sys/file.h>
#include <sys/rmcadm.h>
static int rmcadm_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg,
void **resultp);
static int rmcadm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
static int rmcadm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
static int rmcadm_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p);
static int rmcadm_close(dev_t dev, int flag, int otyp, cred_t *cred_p);
static int rmcadm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
cred_t *cred_p, int *rval_p);
static struct cb_ops rmcadm_cb_ops = {
rmcadm_open,
rmcadm_close,
nodev,
nodev,
nodev,
nodev,
nodev,
rmcadm_ioctl,
nodev,
nodev,
ddi_segmap,
nochpoll,
ddi_prop_op,
NULL,
D_NEW | D_MP
};
static struct dev_ops rmcadm_ops = {
DEVO_REV,
0,
rmcadm_getinfo,
nulldev,
nulldev,
rmcadm_attach,
rmcadm_detach,
nodev,
&rmcadm_cb_ops,
(struct bus_ops *)NULL,
nulldev,
ddi_quiesce_not_needed,
};
extern struct mod_ops mod_driverops;
static struct modldrv modldrv = {
&mod_driverops,
"rmcadm control driver",
&rmcadm_ops
};
static struct modlinkage modlinkage = {
MODREV_1,
&modldrv,
NULL
};
static dev_info_t *rmcadm_dip = NULL;
extern void pmugpio_reset();
int
rmcadm_get_errno(int status)
{
int retval = EIO;
switch (status) {
case RCENOSOFTSTATE:
retval = EIO;
break;
case RCENODATALINK:
retval = EIO;
break;
case RCENOMEM:
retval = ENOMEM;
break;
case RCECANTRESEND:
retval = EIO;
break;
case RCEMAXRETRIES:
retval = EINTR;
break;
case RCETIMEOUT:
retval = EINTR;
break;
case RCEINVCMD:
retval = ENOTSUP;
break;
case RCEINVARG:
retval = ENOTSUP;
break;
case RCEGENERIC:
retval = EIO;
break;
default:
retval = EIO;
break;
}
return (retval);
}
int
_init(void)
{
int error = 0;
error = mod_install(&modlinkage);
return (error);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
int
_fini(void)
{
int error = 0;
error = mod_remove(&modlinkage);
if (error)
return (error);
return (error);
}
static int
rmcadm_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
{
minor_t m = getminor((dev_t)arg);
switch (cmd) {
case DDI_INFO_DEVT2DEVINFO:
if ((m != 0) || (rmcadm_dip == NULL)) {
*resultp = NULL;
return (DDI_FAILURE);
}
*resultp = rmcadm_dip;
return (DDI_SUCCESS);
case DDI_INFO_DEVT2INSTANCE:
*resultp = (void *)(uintptr_t)m;
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
static int
rmcadm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
int instance;
int err;
switch (cmd) {
case DDI_ATTACH:
instance = ddi_get_instance(dip);
if (instance != 0)
return (DDI_FAILURE);
err = ddi_create_minor_node(dip, "rmcadm", S_IFCHR,
instance, DDI_PSEUDO, 0);
if (err != DDI_SUCCESS)
return (DDI_FAILURE);
err = rmc_comm_register();
if (err != DDI_SUCCESS) {
ddi_remove_minor_node(dip, NULL);
return (DDI_FAILURE);
}
rmcadm_dip = dip;
ddi_report_dev(dip);
return (DDI_SUCCESS);
case DDI_RESUME:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
static int
rmcadm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
int instance;
switch (cmd) {
case DDI_DETACH:
instance = ddi_get_instance(dip);
if (instance != 0)
return (DDI_FAILURE);
rmcadm_dip = NULL;
ddi_remove_minor_node(dip, NULL);
rmc_comm_unregister();
return (DDI_SUCCESS);
case DDI_SUSPEND:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
static int
rmcadm_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p)
{
int error = 0;
int instance = getminor(*dev_p);
if (instance != 0)
return (ENXIO);
if ((error = drv_priv(cred_p)) != 0) {
cmn_err(CE_WARN, "rmcadm: inst %d drv_priv failed",
instance);
return (error);
}
return (error);
}
static int
rmcadm_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
{
return (DDI_SUCCESS);
}
static int
rmcadm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred_p,
int *rval_p)
{
int instance = getminor(dev);
int retval = 0;
rmcadm_request_response_t rr;
rmcadm_send_srecord_bp_t ssbp;
rmc_comm_msg_t rmc_req, *rmc_reqp = &rmc_req;
rmc_comm_msg_t rmc_resp, *rmc_respp = &rmc_resp;
caddr_t user_req_buf;
caddr_t user_data_buf;
caddr_t user_resp_buf;
if (instance != 0)
return (ENXIO);
switch (cmd) {
case RMCADM_REQUEST_RESPONSE:
case RMCADM_REQUEST_RESPONSE_BP:
#ifdef _MULTI_DATAMODEL
switch (ddi_model_convert_from(mode & FMODELS)) {
case DDI_MODEL_ILP32:
{
rmcadm_request_response32_t rr32;
if (ddi_copyin((caddr_t)arg, (caddr_t)&rr32,
sizeof (rr32), mode)) {
return (EFAULT);
}
rr.req.msg_type = rr32.req.msg_type;
rr.req.msg_len = rr32.req.msg_len;
rr.req.msg_bytes = rr32.req.msg_bytes;
rr.req.msg_buf = (caddr_t)(uintptr_t)rr32.req.msg_buf;
rr.resp.msg_type = rr32.resp.msg_type;
rr.resp.msg_len = rr32.resp.msg_len;
rr.resp.msg_bytes = rr32.resp.msg_bytes;
rr.resp.msg_buf = (caddr_t)(uintptr_t)rr32.resp.msg_buf;
rr.wait_time = rr32.wait_time;
break;
}
case DDI_MODEL_NONE:
if (ddi_copyin((caddr_t)arg, (caddr_t)&rr,
sizeof (rr), mode)) {
return (EFAULT);
}
break;
}
#else
if (ddi_copyin((caddr_t)arg, (caddr_t)&rr,
sizeof (rr), mode) != 0) {
return (EFAULT);
}
#endif
user_req_buf = rr.req.msg_buf;
if (user_req_buf != NULL) {
rr.req.msg_buf = kmem_alloc(rr.req.msg_len, KM_SLEEP);
if (ddi_copyin(user_req_buf, rr.req.msg_buf,
rr.req.msg_len, mode) != 0) {
kmem_free(rr.req.msg_buf, rr.req.msg_len);
rr.req.msg_buf = user_req_buf;
return (EFAULT);
}
} else {
if (rr.req.msg_len > 0)
return (EINVAL);
}
user_resp_buf = rr.resp.msg_buf;
if (user_resp_buf != NULL) {
rr.resp.msg_buf = kmem_alloc(rr.resp.msg_len, KM_SLEEP);
}
rmc_reqp->msg_type = rr.req.msg_type;
rmc_reqp->msg_buf = rr.req.msg_buf;
rmc_reqp->msg_len = rr.req.msg_len;
rmc_reqp->msg_bytes = rr.req.msg_bytes;
if (cmd == RMCADM_REQUEST_RESPONSE) {
if (rr.resp.msg_type != DP_NULL_MSG) {
rmc_respp->msg_type = rr.resp.msg_type;
rmc_respp->msg_buf = rr.resp.msg_buf;
rmc_respp->msg_len = rr.resp.msg_len;
rmc_respp->msg_bytes = rr.resp.msg_bytes;
} else {
rmc_respp = (rmc_comm_msg_t *)NULL;
}
rr.status = rmc_comm_request_response(
rmc_reqp, rmc_respp, rr.wait_time);
} else {
if (rr.resp.msg_buf != NULL) {
rmc_respp->msg_type = rr.resp.msg_type;
rmc_respp->msg_buf = rr.resp.msg_buf;
rmc_respp->msg_len = rr.resp.msg_len;
rmc_respp->msg_bytes = rr.resp.msg_bytes;
} else {
rmc_respp = (rmc_comm_msg_t *)NULL;
}
rr.status = rmc_comm_request_response_bp(
rmc_reqp, rmc_respp, rr.wait_time);
}
if (rmc_respp != NULL) {
rr.resp.msg_bytes = rmc_respp->msg_bytes;
}
if (rr.status != RCNOERR) {
retval = rmcadm_get_errno(rr.status);
} else if (user_resp_buf != NULL) {
if (ddi_copyout(rr.resp.msg_buf, user_resp_buf,
rr.resp.msg_bytes, mode) != 0) {
retval = EFAULT;
}
}
if (rr.req.msg_buf)
kmem_free(rr.req.msg_buf, rr.req.msg_len);
if (rr.resp.msg_buf)
kmem_free(rr.resp.msg_buf, rr.resp.msg_len);
rr.req.msg_buf = user_req_buf;
rr.resp.msg_buf = user_resp_buf;
#ifdef _MULTI_DATAMODEL
switch (ddi_model_convert_from(mode & FMODELS)) {
case DDI_MODEL_ILP32:
{
rmcadm_request_response32_t rr32;
rr32.req.msg_type = rr.req.msg_type;
rr32.req.msg_len = rr.req.msg_len;
rr32.req.msg_bytes = rr.req.msg_bytes;
rr32.req.msg_buf = (caddr32_t)(uintptr_t)rr.req.msg_buf;
rr32.resp.msg_type = rr.resp.msg_type;
rr32.resp.msg_len = rr.resp.msg_len;
rr32.resp.msg_bytes = rr.resp.msg_bytes;
rr32.resp.msg_buf =
(caddr32_t)(uintptr_t)rr.resp.msg_buf;
rr32.wait_time = rr.wait_time;
rr32.status = rr.status;
if (ddi_copyout((caddr_t)&rr32, (caddr_t)arg,
sizeof (rr32), mode)) {
return (EFAULT);
}
break;
}
case DDI_MODEL_NONE:
if (ddi_copyout((caddr_t)&rr, (caddr_t)arg,
sizeof (rr), mode))
return (EFAULT);
break;
}
#else
if (ddi_copyout((caddr_t)&rr, (caddr_t)arg, sizeof (rr),
mode) != 0)
return (EFAULT);
#endif
break;
case RMCADM_SEND_SRECORD_BP:
#ifdef _MULTI_DATAMODEL
switch (ddi_model_convert_from(mode & FMODELS)) {
case DDI_MODEL_ILP32:
{
rmcadm_send_srecord_bp32_t ssbp32;
if (ddi_copyin((caddr_t)arg, (caddr_t)&ssbp32,
sizeof (ssbp32), mode)) {
return (EFAULT);
}
ssbp.data_len = ssbp32.data_len;
ssbp.data_buf = (caddr_t)(uintptr_t)ssbp32.data_buf;
ssbp.resp_bp.msg_type = ssbp32.resp_bp.msg_type;
ssbp.resp_bp.msg_len = ssbp32.resp_bp.msg_len;
ssbp.resp_bp.msg_bytes = ssbp32.resp_bp.msg_bytes;
ssbp.resp_bp.msg_buf =
(caddr_t)(uintptr_t)ssbp32.resp_bp.msg_buf;
ssbp.wait_time = ssbp32.wait_time;
break;
}
case DDI_MODEL_NONE:
if (ddi_copyin((caddr_t)arg, (caddr_t)&ssbp,
sizeof (ssbp), mode))
return (EFAULT);
break;
}
#else
if (ddi_copyin((caddr_t)arg, (caddr_t)&ssbp,
sizeof (ssbp), mode) != 0)
return (EFAULT);
#endif
user_data_buf = ssbp.data_buf;
if (user_data_buf != NULL) {
ssbp.data_buf = kmem_alloc(ssbp.data_len, KM_SLEEP);
if (ddi_copyin(user_data_buf, ssbp.data_buf,
ssbp.data_len, mode) != 0) {
kmem_free(ssbp.data_buf, ssbp.data_len);
ssbp.data_buf = user_data_buf;
return (EFAULT);
}
} else {
return (EINVAL);
}
user_resp_buf = ssbp.resp_bp.msg_buf;
if (user_resp_buf != NULL) {
ssbp.resp_bp.msg_buf =
kmem_alloc(ssbp.resp_bp.msg_len, KM_SLEEP);
} else {
kmem_free(ssbp.data_buf, ssbp.data_len);
return (EINVAL);
}
rmc_respp->msg_type = ssbp.resp_bp.msg_type;
rmc_respp->msg_buf = ssbp.resp_bp.msg_buf;
rmc_respp->msg_len = ssbp.resp_bp.msg_len;
rmc_respp->msg_bytes = ssbp.resp_bp.msg_bytes;
ssbp.status = rmc_comm_send_srecord_bp(ssbp.data_buf,
ssbp.data_len, rmc_respp, ssbp.wait_time);
ssbp.resp_bp.msg_bytes = rmc_respp->msg_bytes;
if (ssbp.status != RCNOERR) {
retval = rmcadm_get_errno(ssbp.status);
} else if (user_resp_buf != NULL) {
if (ddi_copyout(ssbp.resp_bp.msg_buf, user_resp_buf,
ssbp.resp_bp.msg_bytes, mode) != 0) {
retval = EFAULT;
}
}
if (ssbp.data_buf)
kmem_free(ssbp.data_buf, ssbp.data_len);
if (ssbp.resp_bp.msg_buf)
kmem_free(ssbp.resp_bp.msg_buf, ssbp.resp_bp.msg_len);
ssbp.data_buf = user_data_buf;
ssbp.resp_bp.msg_buf = user_resp_buf;
#ifdef _MULTI_DATAMODEL
switch (ddi_model_convert_from(mode & FMODELS)) {
case DDI_MODEL_ILP32:
{
rmcadm_send_srecord_bp32_t ssbp32;
ssbp32.data_len = ssbp.data_len;
ssbp32.data_buf = (caddr32_t)(uintptr_t)ssbp.data_buf;
ssbp32.resp_bp.msg_type = ssbp.resp_bp.msg_type;
ssbp32.resp_bp.msg_len = ssbp.resp_bp.msg_len;
ssbp32.resp_bp.msg_bytes = ssbp.resp_bp.msg_bytes;
ssbp32.resp_bp.msg_buf =
(caddr32_t)(uintptr_t)ssbp.resp_bp.msg_buf;
ssbp32.wait_time = ssbp.wait_time;
if (ddi_copyout((caddr_t)&ssbp32, (caddr_t)arg,
sizeof (ssbp32), mode)) {
return (EFAULT);
}
break;
}
case DDI_MODEL_NONE:
if (ddi_copyout((caddr_t)&ssbp, (caddr_t)arg,
sizeof (ssbp), mode))
return (EFAULT);
break;
}
#else
if (ddi_copyout((caddr_t)&ssbp, (caddr_t)arg, sizeof (ssbp),
mode) != 0)
return (EFAULT);
#endif
break;
case RMCADM_RESET_SP:
pmugpio_reset();
retval = 0;
break;
default:
retval = ENOTSUP;
break;
}
return (retval);
}