#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/dlpi.h>
#include <sys/stat.h>
#include <sys/strsun.h>
#include <sys/stropts.h>
#include <sys/types.h>
#include <sys/id_space.h>
#include <sys/sysmacros.h>
#include <sys/kmem.h>
#include <sys/modctl.h>
#include <sys/mkdev.h>
#include <sys/sdt.h>
#include "dlpistub_impl.h"
static id_space_t *ds_minors;
static dev_info_t *ds_dip;
static dl_info_ack_t ds_infoack = {
DL_INFO_ACK,
0,
0,
0,
0,
0,
0,
0,
DL_CLDLS,
0,
0,
0,
0,
DL_STYLE2,
0,
DL_VERSION_2,
0,
0,
0
};
static int
ds_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
if (cmd != DDI_ATTACH)
return (DDI_FAILURE);
if (ddi_create_minor_node(dip, "vni", S_IFCHR, DS_MINOR_VNI,
DDI_PSEUDO, 0) == DDI_FAILURE ||
ddi_create_minor_node(dip, "ipmpstub", S_IFCHR, DS_MINOR_IPMP,
DDI_PSEUDO, 0) == DDI_FAILURE) {
ddi_remove_minor_node(dip, NULL);
cmn_err(CE_NOTE, "ds_attach: cannot create minor nodes");
return (DDI_FAILURE);
}
ds_dip = dip;
ds_minors = id_space_create("ds_minors", DS_MINOR_START, MAXMIN32);
return (DDI_SUCCESS);
}
static int
ds_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
if (cmd != DDI_DETACH)
return (DDI_FAILURE);
id_space_destroy(ds_minors);
ds_minors = NULL;
ASSERT(dip == ds_dip);
ddi_remove_minor_node(dip, NULL);
ds_dip = NULL;
return (DDI_SUCCESS);
}
static int
ds_devinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
{
int error = DDI_FAILURE;
switch (infocmd) {
case DDI_INFO_DEVT2INSTANCE:
*result = (void *)0;
error = DDI_SUCCESS;
break;
case DDI_INFO_DEVT2DEVINFO:
if (ds_dip != NULL) {
*result = ds_dip;
error = DDI_SUCCESS;
}
break;
}
return (error);
}
static int
ds_open(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *credp)
{
int type;
dlpistub_t *dsp;
if (sflag == CLONEOPEN || sflag == MODOPEN)
return (EINVAL);
if (q->q_ptr != NULL)
return (0);
switch (getminor(*devp)) {
case DS_MINOR_VNI:
type = SUNW_DL_VNI;
break;
case DS_MINOR_IPMP:
type = SUNW_DL_IPMP;
break;
default:
return (ENXIO);
}
dsp = kmem_zalloc(sizeof (dlpistub_t), KM_SLEEP);
dsp->ds_type = type;
dsp->ds_minor = (minor_t)id_alloc(ds_minors);
dsp->ds_state = DL_UNATTACHED;
*devp = makedevice(getmajor(*devp), dsp->ds_minor);
q->q_ptr = WR(q)->q_ptr = dsp;
qprocson(q);
return (0);
}
static int
ds_close(queue_t *q, int flag, cred_t *credp)
{
dlpistub_t *dsp = q->q_ptr;
qprocsoff(q);
q->q_ptr = WR(q)->q_ptr = NULL;
id_free(ds_minors, dsp->ds_minor);
kmem_free(dsp, sizeof (dlpistub_t));
return (0);
}
static int
ds_badprim(queue_t *q, mblk_t *mp, t_scalar_t prim)
{
dlerrorack(q, mp, prim, DL_BADPRIM, 0);
return (0);
}
static int
ds_outstate(queue_t *q, mblk_t *mp, t_scalar_t prim)
{
dlerrorack(q, mp, prim, DL_OUTSTATE, 0);
return (0);
}
static int
ds_wput(queue_t *q, mblk_t *mp)
{
union DL_primitives *dlp;
dl_info_ack_t *dlip;
dlpistub_t *dsp = q->q_ptr;
t_scalar_t prim;
switch (DB_TYPE(mp)) {
case M_PROTO:
case M_PCPROTO:
if (MBLKL(mp) < sizeof (t_scalar_t)) {
dlerrorack(q, mp, DL_PRIM_INVAL, DL_UNSUPPORTED, 0);
return (0);
}
dlp = (void *)mp->b_rptr;
prim = dlp->dl_primitive;
switch (prim) {
case DL_ATTACH_REQ:
if (MBLKL(mp) < DL_ATTACH_REQ_SIZE)
return (ds_badprim(q, mp, prim));
if (dsp->ds_state != DL_UNATTACHED)
return (ds_outstate(q, mp, prim));
dsp->ds_state = DL_UNBOUND;
dlokack(q, mp, DL_ATTACH_REQ);
break;
case DL_BIND_REQ:
if (MBLKL(mp) < DL_BIND_REQ_SIZE)
return (ds_badprim(q, mp, prim));
if (dsp->ds_state != DL_UNBOUND)
return (ds_outstate(q, mp, prim));
dsp->ds_state = DL_IDLE;
dlbindack(q, mp, dlp->bind_req.dl_sap, NULL, 0, 0, 0);
break;
case DL_INFO_REQ:
if (MBLKL(mp) < DL_INFO_REQ_SIZE)
return (ds_badprim(q, mp, prim));
mp = mexchange(q, mp, sizeof (dl_info_ack_t),
M_PCPROTO, DL_INFO_ACK);
if (mp != NULL) {
dlip = (void *)mp->b_rptr;
*dlip = ds_infoack;
dlip->dl_mac_type = dsp->ds_type;
dlip->dl_current_state = dsp->ds_state;
qreply(q, mp);
}
break;
case DL_PHYS_ADDR_REQ:
if (MBLKL(mp) < DL_PHYS_ADDR_REQ_SIZE)
return (ds_badprim(q, mp, prim));
dlphysaddrack(q, mp, NULL, 0);
break;
case DL_UNBIND_REQ:
if (MBLKL(mp) < DL_UNBIND_REQ_SIZE)
return (ds_badprim(q, mp, prim));
if (dsp->ds_state != DL_IDLE)
return (ds_outstate(q, mp, prim));
dsp->ds_state = DL_UNBOUND;
dlokack(q, mp, DL_UNBIND_REQ);
break;
case DL_DETACH_REQ:
if (MBLKL(mp) < DL_DETACH_REQ_SIZE)
return (ds_badprim(q, mp, prim));
if (dsp->ds_state != DL_UNBOUND)
return (ds_outstate(q, mp, prim));
dsp->ds_state = DL_UNATTACHED;
dlokack(q, mp, DL_DETACH_REQ);
break;
case DL_UNITDATA_REQ:
DTRACE_PROBE2(dlpistub__data, dlpistub_t *, dsp,
mblk_t *, mp);
freemsg(mp);
break;
default:
dlerrorack(q, mp, prim, DL_UNSUPPORTED, 0);
}
break;
case M_IOCTL:
miocnak(q, mp, 0, EINVAL);
break;
case M_FLUSH:
*mp->b_rptr &= ~FLUSHW;
if (*mp->b_rptr & FLUSHR)
qreply(q, mp);
else
freemsg(mp);
break;
default:
freemsg(mp);
break;
}
return (0);
}
static struct module_info ds_minfo = {
DS_IDNUM,
"dlpistub",
0,
INFPSZ,
0,
0,
};
static struct qinit ds_rinit = {
NULL,
NULL,
ds_open,
ds_close,
NULL,
&ds_minfo,
};
static struct qinit ds_winit = {
ds_wput,
NULL,
NULL,
NULL,
NULL,
&ds_minfo,
};
static struct streamtab ds_info = {
&ds_rinit,
&ds_winit
};
DDI_DEFINE_STREAM_OPS(ds_ops, nulldev, nulldev, ds_attach, ds_detach,
nodev, ds_devinfo, D_MP|D_MTPERMOD, &ds_info, ddi_quiesce_not_supported);
static struct modldrv modldrv = {
&mod_driverops,
"DLPI stub driver",
&ds_ops,
};
static struct modlinkage modlinkage = {
MODREV_1, &modldrv, NULL
};
int
_init(void)
{
return (mod_install(&modlinkage));
}
int
_fini(void)
{
return (mod_remove(&modlinkage));
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}