#include <sys/types.h>
#include <sys/conf.h>
#include <sys/devops.h>
#include <sys/kmem.h>
#include <sys/ksynch.h>
#include <sys/modctl.h>
#include <sys/stat.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/sunndi.h>
#include <sys/ib/clients/eoib/enx_impl.h>
eibnx_t *enx_global_ss = NULL;
static int eibnx_attach(dev_info_t *, ddi_attach_cmd_t);
static int eibnx_detach(dev_info_t *, ddi_detach_cmd_t);
static int eibnx_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
static int eibnx_bus_ctl(dev_info_t *, dev_info_t *, ddi_ctl_enum_t,
void *, void *);
static int eibnx_get_eventcookie(dev_info_t *, dev_info_t *, char *,
ddi_eventcookie_t *);
static int eibnx_add_eventcall(dev_info_t *, dev_info_t *, ddi_eventcookie_t,
void (*)(dev_info_t *, ddi_eventcookie_t, void *, void *),
void *, ddi_callback_id_t *);
static int eibnx_remove_eventcall(dev_info_t *, ddi_callback_id_t);
static int eibnx_post_event(dev_info_t *, dev_info_t *,
ddi_eventcookie_t, void *);
static int eibnx_bus_config(dev_info_t *, uint_t, ddi_bus_config_op_t,
void *, dev_info_t **);
static int eibnx_bus_unconfig(dev_info_t *, uint_t, ddi_bus_config_op_t,
void *);
static int eibnx_config_all_children(dev_info_t *);
static void eibnx_unconfig_all_children(dev_info_t *);
static int eibnx_config_child(char *, dev_info_t **);
static int eibnx_unconfig_child(char *);
static struct cb_ops enx_cb_ops = {
eibnx_devctl_open,
eibnx_devctl_close,
nodev,
nodev,
nodev,
nodev,
nodev,
eibnx_devctl_ioctl,
nodev,
nodev,
nodev,
nochpoll,
ddi_prop_op,
NULL,
D_MP,
CB_REV,
nodev,
nodev
};
static struct bus_ops enx_bus_ops = {
BUSO_REV,
nullbusmap,
NULL,
NULL,
NULL,
i_ddi_map_fault,
ddi_no_dma_map,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
eibnx_bus_ctl,
ddi_bus_prop_op,
eibnx_get_eventcookie,
eibnx_add_eventcall,
eibnx_remove_eventcall,
eibnx_post_event,
NULL,
eibnx_bus_config,
eibnx_bus_unconfig,
};
static struct dev_ops enx_ops = {
DEVO_REV,
0,
eibnx_getinfo,
nulldev,
nulldev,
eibnx_attach,
eibnx_detach,
nodev,
&enx_cb_ops,
&enx_bus_ops,
nulldev,
ddi_quiesce_not_needed
};
static struct modldrv enx_modldrv = {
&mod_driverops,
"EoIB Nexus",
&enx_ops,
};
static struct modlinkage enx_modlinkage = {
MODREV_1, (void *)&enx_modldrv, NULL
};
static ndi_event_definition_t enx_ndi_event_defs[] = {
{ ENX_EVENT_TAG_GW_INFO_UPDATE, EIB_NDI_EVENT_GW_INFO_UPDATE,
EPL_KERNEL, NDI_EVENT_POST_TO_TGT },
{ ENX_EVENT_TAG_GW_AVAILABLE, EIB_NDI_EVENT_GW_AVAILABLE,
EPL_KERNEL, NDI_EVENT_POST_TO_TGT },
{ ENX_EVENT_TAG_LOGIN_ACK, EIB_NDI_EVENT_LOGIN_ACK,
EPL_KERNEL, NDI_EVENT_POST_TO_TGT }
};
#define ENX_NUM_NDI_EVENTS \
(sizeof (enx_ndi_event_defs) / sizeof (enx_ndi_event_defs[0]))
static ndi_event_set_t enx_ndi_events = {
NDI_EVENTS_REV1,
ENX_NUM_NDI_EVENTS,
enx_ndi_event_defs
};
ndi_event_hdl_t enx_ndi_event_hdl;
int
_init(void)
{
int ret;
if ((ret = mod_install(&enx_modlinkage)) == 0)
eibnx_debug_init();
return (ret);
}
int
_fini(void)
{
int ret;
if ((ret = mod_remove(&enx_modlinkage)) == 0)
eibnx_debug_fini();
return (ret);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&enx_modlinkage, modinfop));
}
static int
eibnx_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
eibnx_t *ss;
int instance;
if (cmd == DDI_RESUME)
return (DDI_SUCCESS);
else if (cmd != DDI_ATTACH)
return (DDI_FAILURE);
if (enx_global_ss)
return (DDI_FAILURE);
ss = kmem_zalloc(sizeof (eibnx_t), KM_SLEEP);
ss->nx_dip = dip;
enx_global_ss = ss;
if (ndi_event_alloc_hdl(dip, 0, &enx_ndi_event_hdl,
NDI_SLEEP) != NDI_SUCCESS) {
ENX_DPRINTF_ERR("ndi_event_alloc_hdl(dip=0x%llx) "
"failed", dip);
kmem_free(enx_global_ss, sizeof (eibnx_t));
enx_global_ss = NULL;
return (DDI_FAILURE);
}
if (ndi_event_bind_set(enx_ndi_event_hdl, &enx_ndi_events,
NDI_SLEEP) != NDI_SUCCESS) {
ENX_DPRINTF_ERR("ndi_event_bind_set(ndi_event_hdl=0x%llx) "
"failed", enx_ndi_event_hdl);
(void) ndi_event_free_hdl(enx_ndi_event_hdl);
enx_ndi_event_hdl = NULL;
kmem_free(enx_global_ss, sizeof (eibnx_t));
enx_global_ss = NULL;
return (DDI_FAILURE);
}
instance = ddi_get_instance(dip);
if (ddi_create_minor_node(dip, "devctl", S_IFCHR, instance,
DDI_NT_NEXUS, 0) != DDI_SUCCESS) {
ENX_DPRINTF_WARN("could not create devctl minor node "
"for instance %d", instance);
}
if (eibnx_ibt_init(ss) != ENX_E_SUCCESS) {
(void) ddi_remove_minor_node(dip, NULL);
(void) ndi_event_unbind_set(enx_ndi_event_hdl,
&enx_ndi_events, NDI_SLEEP);
(void) ndi_event_free_hdl(enx_ndi_event_hdl);
enx_ndi_event_hdl = NULL;
kmem_free(enx_global_ss, sizeof (eibnx_t));
enx_global_ss = NULL;
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
static int
eibnx_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
eibnx_t *ss = enx_global_ss;
if (cmd == DDI_SUSPEND)
return (DDI_SUCCESS);
else if (cmd != DDI_DETACH)
return (DDI_FAILURE);
if (ss == NULL)
return (DDI_FAILURE);
eibnx_terminate_monitors();
if (eibnx_ibt_fini(ss) == ENX_E_FAILURE)
return (DDI_FAILURE);
(void) ddi_remove_minor_node(dip, NULL);
(void) ndi_event_unbind_set(enx_ndi_event_hdl,
&enx_ndi_events, NDI_SLEEP);
(void) ndi_event_free_hdl(enx_ndi_event_hdl);
enx_ndi_event_hdl = NULL;
kmem_free(enx_global_ss, sizeof (eibnx_t));
enx_global_ss = NULL;
return (DDI_SUCCESS);
}
static int
eibnx_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
{
eibnx_t *ss = enx_global_ss;
int ret;
if (cmd == DDI_INFO_DEVT2DEVINFO) {
*resultp = (ss) ? ss->nx_dip : NULL;
ret = (ss) ? DDI_SUCCESS : DDI_FAILURE;
} else if (cmd == DDI_INFO_DEVT2INSTANCE) {
*resultp = 0;
ret = DDI_SUCCESS;
} else {
ret = DDI_FAILURE;
}
return (ret);
}
static int
eibnx_bus_ctl(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t ctlop,
void *arg, void *result)
{
dev_info_t *child = arg;
int ret;
char name[MAXNAMELEN];
switch (ctlop) {
case DDI_CTLOPS_REPORTDEV:
ENX_DPRINTF_DEBUG("EoIB device: %s@%s, %s%d",
ddi_node_name(rdip), ddi_get_name_addr(rdip),
ddi_driver_name(rdip), ddi_get_instance(rdip));
case DDI_CTLOPS_ATTACH:
case DDI_CTLOPS_DETACH:
case DDI_CTLOPS_POWER:
case DDI_CTLOPS_SIDDEV:
case DDI_CTLOPS_IOMIN:
ret = DDI_SUCCESS;
break;
case DDI_CTLOPS_INITCHILD:
if ((ret = eibnx_name_child(child, name,
sizeof (name))) == DDI_SUCCESS) {
ddi_set_name_addr(child, name);
}
break;
case DDI_CTLOPS_UNINITCHILD:
ddi_set_name_addr(child, NULL);
ret = DDI_SUCCESS;
break;
default:
ret = ddi_ctlops(dip, rdip, ctlop, arg, result);
break;
}
return (ret);
}
static int
eibnx_bus_config(dev_info_t *parent, uint_t flags,
ddi_bus_config_op_t op, void *arg, dev_info_t **childp)
{
eibnx_t *ss = enx_global_ss;
int ret = NDI_SUCCESS;
switch (op) {
case BUS_CONFIG_ONE:
eibnx_busop_inprog_enter(ss);
ret = eibnx_config_child(arg, childp);
eibnx_busop_inprog_exit(ss);
break;
case BUS_CONFIG_ALL:
case BUS_CONFIG_DRIVER:
eibnx_busop_inprog_enter(ss);
if ((ss->nx_busop_flags & NX_FL_BUSCFG_COMPLETE) == 0) {
ret = eibnx_config_all_children(parent);
if (ret == NDI_SUCCESS)
ss->nx_busop_flags |= NX_FL_BUSCFG_COMPLETE;
}
eibnx_busop_inprog_exit(ss);
break;
default:
ret = NDI_FAILURE;
}
if (ret == NDI_SUCCESS)
ret = ndi_busop_bus_config(parent, flags, op, arg, childp, 0);
return (ret);
}
static int
eibnx_bus_unconfig(dev_info_t *parent, uint_t flags,
ddi_bus_config_op_t op, void *arg)
{
eibnx_t *ss = enx_global_ss;
int ret;
ret = ndi_busop_bus_unconfig(parent, flags, op, arg);
if (ret != NDI_SUCCESS)
return (ret);
switch (op) {
case BUS_UNCONFIG_ONE:
if (flags & (NDI_UNCONFIG | NDI_DEVI_REMOVE)) {
eibnx_busop_inprog_enter(ss);
if ((ret = eibnx_unconfig_child(arg)) == ENX_E_SUCCESS)
ss->nx_busop_flags &= (~NX_FL_BUSCFG_COMPLETE);
else {
ENX_DPRINTF_DEBUG("eibnx_bus_config: "
"unconfig child %s failed", (char *)arg);
}
eibnx_busop_inprog_exit(ss);
}
break;
case BUS_UNCONFIG_ALL:
case BUS_UNCONFIG_DRIVER:
if (flags & (NDI_UNCONFIG | NDI_DEVI_REMOVE)) {
eibnx_busop_inprog_enter(ss);
eibnx_unconfig_all_children(parent);
ss->nx_busop_flags &= (~NX_FL_BUSCFG_COMPLETE);
eibnx_busop_inprog_exit(ss);
}
break;
default:
break;
}
return (ret);
}
static int
eibnx_get_eventcookie(dev_info_t *dip, dev_info_t *rdip,
char *name, ddi_eventcookie_t *cookiep)
{
return (ndi_event_retrieve_cookie(enx_ndi_event_hdl, rdip, name,
cookiep, NDI_EVENT_NOPASS));
}
static int
eibnx_add_eventcall(dev_info_t *dip, dev_info_t *rdip, ddi_eventcookie_t cookie,
void (*callback)(dev_info_t *cb_dip, ddi_eventcookie_t cb_cookie,
void *cb_arg, void *cb_impl_data),
void *arg, ddi_callback_id_t *cb_id)
{
return (ndi_event_add_callback(enx_ndi_event_hdl, rdip, cookie,
callback, arg, NDI_SLEEP, cb_id));
}
static int
eibnx_remove_eventcall(dev_info_t *dip, ddi_callback_id_t cb_id)
{
return (ndi_event_remove_callback(enx_ndi_event_hdl, cb_id));
}
static int
eibnx_post_event(dev_info_t *dip, dev_info_t *rdip,
ddi_eventcookie_t cookie, void *impl_data)
{
return (ndi_event_run_callbacks(enx_ndi_event_hdl, rdip, cookie,
impl_data));
}
static int
eibnx_config_all_children(dev_info_t *parent)
{
eibnx_t *ss = enx_global_ss;
eibnx_hca_t *hca;
eibnx_port_t *port;
eibnx_thr_info_t *ti;
eibnx_thr_info_t *ti_tail;
eibnx_gw_info_t *gwi;
mutex_enter(&ss->nx_lock);
if (!ss->nx_monitors_up) {
ss->nx_thr_info = ti_tail = NULL;
for (hca = ss->nx_hca; hca; hca = hca->hc_next) {
for (port = hca->hc_port; port; port = port->po_next) {
ti = eibnx_start_port_monitor(hca, port);
if (ti_tail) {
ti_tail->ti_next = ti;
} else {
ss->nx_thr_info = ti;
}
ti_tail = ti;
}
}
ss->nx_monitors_up = B_TRUE;
mutex_exit(&ss->nx_lock);
return (NDI_SUCCESS);
}
mutex_exit(&ss->nx_lock);
while (eibnx_locate_unconfigured_node(&ti, &gwi) == ENX_E_SUCCESS)
(void) eibnx_configure_node(ti, gwi, NULL);
return (NDI_SUCCESS);
}
static void
eibnx_unconfig_all_children(dev_info_t *parent)
{
eibnx_t *ss = enx_global_ss;
eibnx_thr_info_t *ti;
eibnx_child_t *ch;
mutex_enter(&ss->nx_lock);
for (ti = ss->nx_thr_info; ti; ti = ti->ti_next) {
mutex_enter(&ti->ti_child_lock);
for (ch = ti->ti_child; ch; ch = ch->ch_next) {
ch->ch_dip = NULL;
}
mutex_exit(&ti->ti_child_lock);
}
mutex_exit(&ss->nx_lock);
}
static int
eibnx_config_child(char *devname, dev_info_t **childp)
{
eibnx_thr_info_t *ti;
eibnx_gw_info_t *gwi;
if (eibnx_locate_node_name(devname, &ti, &gwi) == ENX_E_FAILURE) {
ENX_DPRINTF_DEBUG("eibnx_config_child: invalid eoib "
"nodename %s, no such address", devname);
return (ENX_E_FAILURE);
}
return (eibnx_configure_node(ti, gwi, childp));
}
static int
eibnx_unconfig_child(char *devname)
{
eibnx_thr_info_t *ti;
eibnx_gw_info_t *gwi;
if (eibnx_locate_node_name(devname, &ti, &gwi) == ENX_E_FAILURE) {
ENX_DPRINTF_DEBUG("eibnx_unconfig_child: invalid eoib "
"nodename %s, no such address", devname);
return (ENX_E_FAILURE);
}
return (eibnx_unconfigure_node(ti, gwi));
}