#include <sys/types.h>
#include <inet/common.h>
#include <sys/stropts.h>
#include <sys/modctl.h>
#include <sys/dld.h>
#include <sys/softmac_impl.h>
dev_info_t *softmac_dip = NULL;
static kmem_cache_t *softmac_upper_cachep;
static int softmac_cmn_open(queue_t *, dev_t *, int, int, cred_t *);
static int softmac_mod_close(queue_t *, int, cred_t *);
static int softmac_mod_rput(queue_t *, mblk_t *);
static int softmac_mod_wput(queue_t *, mblk_t *);
static int softmac_mod_wsrv(queue_t *);
static int softmac_drv_open(queue_t *, dev_t *, int, int, cred_t *);
static int softmac_drv_close(queue_t *, int, cred_t *);
static int softmac_drv_wput(queue_t *, mblk_t *);
static int softmac_drv_wsrv(queue_t *);
static int softmac_attach(dev_info_t *, ddi_attach_cmd_t);
static int softmac_detach(dev_info_t *, ddi_detach_cmd_t);
static int softmac_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
static struct module_info softmac_modinfo = {
0,
SOFTMAC_DEV_NAME,
0,
INFPSZ,
65536,
1024
};
static struct module_info softmac_dld_modinfo = {
0,
SOFTMAC_DEV_NAME,
0,
INFPSZ,
1,
0
};
static struct qinit softmac_urinit = {
softmac_mod_rput,
NULL,
softmac_cmn_open,
softmac_mod_close,
NULL,
&softmac_modinfo
};
static struct qinit softmac_uwinit = {
softmac_mod_wput,
softmac_mod_wsrv,
NULL,
NULL,
NULL,
&softmac_modinfo
};
static struct streamtab softmac_tab = {
&softmac_urinit,
&softmac_uwinit
};
DDI_DEFINE_STREAM_OPS(softmac_ops, nulldev, nulldev, softmac_attach,
softmac_detach, nodev, softmac_info, D_MP, &softmac_tab,
ddi_quiesce_not_supported);
static struct qinit softmac_dld_r_qinit = {
NULL, NULL, softmac_drv_open, softmac_drv_close, NULL,
&softmac_dld_modinfo
};
static struct qinit softmac_dld_w_qinit = {
softmac_drv_wput, softmac_drv_wsrv, NULL, NULL, NULL,
&softmac_dld_modinfo
};
static struct fmodsw softmac_fmodsw = {
SOFTMAC_DEV_NAME,
&softmac_tab,
D_MP
};
static struct modldrv softmac_modldrv = {
&mod_driverops,
"softmac driver",
&softmac_ops
};
static struct modlstrmod softmac_modlstrmod = {
&mod_strmodops,
"softmac module",
&softmac_fmodsw
};
static struct modlinkage softmac_modlinkage = {
MODREV_1,
&softmac_modlstrmod,
&softmac_modldrv,
NULL
};
static void softmac_dedicated_rx(void *, mac_resource_handle_t, mblk_t *,
mac_header_info_t *);
static int
softmac_upper_constructor(void *buf, void *arg, int kmflag)
{
softmac_upper_t *sup = buf;
bzero(buf, sizeof (softmac_upper_t));
mutex_init(&sup->su_mutex, NULL, MUTEX_DEFAULT, NULL);
cv_init(&sup->su_cv, NULL, CV_DEFAULT, NULL);
mutex_init(&sup->su_disp_mutex, NULL, MUTEX_DEFAULT, NULL);
cv_init(&sup->su_disp_cv, NULL, CV_DEFAULT, NULL);
list_create(&sup->su_req_list, sizeof (softmac_switch_req_t),
offsetof(softmac_switch_req_t, ssq_req_list_node));
return (0);
}
static void
softmac_upper_destructor(void *buf, void *arg)
{
softmac_upper_t *sup = buf;
ASSERT(sup->su_slp == NULL);
ASSERT(sup->su_pending_head == NULL && sup->su_pending_tail == NULL);
ASSERT(!sup->su_dlpi_pending);
ASSERT(!sup->su_active);
ASSERT(!sup->su_closing);
ASSERT(sup->su_tx_flow_mp == NULL);
ASSERT(sup->su_tx_inprocess == 0);
ASSERT(sup->su_mode == SOFTMAC_UNKNOWN);
ASSERT(!sup->su_tx_busy);
ASSERT(!sup->su_bound);
ASSERT(!sup->su_taskq_scheduled);
ASSERT(sup->su_tx_notify_func == NULL);
ASSERT(sup->su_tx_notify_arg == NULL);
ASSERT(list_is_empty(&sup->su_req_list));
list_destroy(&sup->su_req_list);
mutex_destroy(&sup->su_mutex);
cv_destroy(&sup->su_cv);
mutex_destroy(&sup->su_disp_mutex);
cv_destroy(&sup->su_disp_cv);
}
int
_init(void)
{
int err;
mac_init_ops(NULL, SOFTMAC_DEV_NAME);
softmac_init();
softmac_upper_cachep = kmem_cache_create("softmac_upper_cache",
sizeof (softmac_upper_t), 0, softmac_upper_constructor,
softmac_upper_destructor, NULL, NULL, NULL, 0);
ASSERT(softmac_upper_cachep != NULL);
if ((err = mod_install(&softmac_modlinkage)) != 0) {
softmac_fini();
return (err);
}
return (0);
}
int
_fini(void)
{
int err;
if (softmac_busy())
return (EBUSY);
if ((err = mod_remove(&softmac_modlinkage)) != 0)
return (err);
kmem_cache_destroy(softmac_upper_cachep);
softmac_fini();
return (0);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&softmac_modlinkage, modinfop));
}
static int
softmac_cmn_open(queue_t *rq, dev_t *devp, int flag, int sflag, cred_t *credp)
{
softmac_lower_t *slp;
if (rq->q_ptr != NULL)
return (EBUSY);
if (sflag == MODOPEN) {
if ((slp = kmem_zalloc(sizeof (*slp), KM_NOSLEEP)) == NULL)
return (ENOMEM);
slp->sl_wq = WR(rq);
cv_init(&slp->sl_cv, NULL, CV_DRIVER, NULL);
mutex_init(&slp->sl_mutex, NULL, MUTEX_DRIVER, NULL);
slp->sl_pending_prim = DL_PRIM_INVAL;
rq->q_ptr = WR(rq)->q_ptr = slp;
qprocson(rq);
return (0);
}
rq->q_qinfo = &softmac_dld_r_qinit;
WR(rq)->q_qinfo = &softmac_dld_w_qinit;
return (softmac_drv_open(rq, devp, flag, sflag, credp));
}
static int
softmac_mod_close(queue_t *rq, int flags __unused, cred_t *credp __unused)
{
softmac_lower_t *slp = rq->q_ptr;
ASSERT(WR(rq)->q_next != NULL);
qprocsoff(rq);
slp->sl_softmac = NULL;
slp->sl_lh = NULL;
ASSERT(slp->sl_ack_mp == NULL);
ASSERT(slp->sl_pending_prim == DL_PRIM_INVAL);
ASSERT(slp->sl_pending_ioctl == B_FALSE);
cv_destroy(&slp->sl_cv);
mutex_destroy(&slp->sl_mutex);
kmem_free(slp, sizeof (*slp));
return (0);
}
static int
softmac_mod_rput(queue_t *rq, mblk_t *mp)
{
softmac_lower_t *slp = rq->q_ptr;
softmac_lower_rxinfo_t *rxinfo;
union DL_primitives *dlp;
ASSERT(WR(rq)->q_next != NULL);
ASSERT((mp->b_next == NULL) && (mp->b_prev == NULL));
switch (DB_TYPE(mp)) {
case M_DATA: {
if ((rxinfo = slp->sl_rxinfo) != NULL) {
rxinfo->slr_rx(rxinfo->slr_arg, NULL, mp, NULL);
break;
}
if (slp->sl_softmac == NULL) {
freemsg(mp);
return (0);
}
if (mp->b_flag & MSGNOLOOP) {
freemsg(mp);
return (0);
}
if (DB_REF(mp) == 1) {
ASSERT(slp->sl_softmac != NULL);
mac_rx(slp->sl_softmac->smac_mh, NULL, mp);
return (0);
} else {
softmac_rput_process_data(slp, mp);
}
break;
}
case M_PROTO:
case M_PCPROTO:
if (MBLKL(mp) < sizeof (dlp->dl_primitive)) {
freemsg(mp);
break;
}
dlp = (union DL_primitives *)mp->b_rptr;
if (dlp->dl_primitive == DL_UNITDATA_IND) {
if ((rxinfo = slp->sl_rxinfo) != NULL) {
softmac_dedicated_rx(slp->sl_sup, NULL, mp,
NULL);
break;
}
cmn_err(CE_WARN, "got unexpected %s message",
dl_primstr(DL_UNITDATA_IND));
freemsg(mp);
break;
}
default:
softmac_rput_process_notdata(rq, slp->sl_sup, mp);
break;
}
return (0);
}
static int
softmac_mod_wput(queue_t *wq, mblk_t *mp)
{
ASSERT(wq->q_next != NULL);
switch (DB_TYPE(mp)) {
case M_IOCTL: {
struct iocblk *ioc = (struct iocblk *)mp->b_rptr;
switch (ioc->ioc_cmd) {
case SMAC_IOC_START: {
softmac_lower_t *slp = wq->q_ptr;
smac_ioc_start_t *arg;
if (ioc->ioc_count != sizeof (*arg)) {
miocnak(wq, mp, 0, EINVAL);
break;
}
arg = (smac_ioc_start_t *)mp->b_cont->b_rptr;
arg->si_slp = slp;
miocack(wq, mp, sizeof (*arg), 0);
break;
}
default:
miocnak(wq, mp, 0, EINVAL);
break;
}
break;
}
default:
freemsg(mp);
break;
}
return (0);
}
static int
softmac_mod_wsrv(queue_t *wq)
{
softmac_lower_t *slp = wq->q_ptr;
ASSERT(wq->q_next != NULL);
if (slp->sl_sup != NULL)
qenable(slp->sl_sup->su_wq);
else if (slp->sl_softmac != NULL)
mac_tx_update(slp->sl_softmac->smac_mh);
return (0);
}
static int
softmac_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
ASSERT(ddi_get_instance(dip) == 0);
if (cmd != DDI_ATTACH)
return (DDI_FAILURE);
softmac_dip = dip;
return (DDI_SUCCESS);
}
static int
softmac_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
if (cmd != DDI_DETACH)
return (DDI_FAILURE);
softmac_dip = NULL;
return (DDI_SUCCESS);
}
static int
softmac_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
{
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
if (softmac_dip != NULL) {
*result = softmac_dip;
return (DDI_SUCCESS);
}
break;
case DDI_INFO_DEVT2INSTANCE:
*result = NULL;
return (DDI_SUCCESS);
}
return (DDI_FAILURE);
}
static void
softmac_dedicated_rx(void *arg, mac_resource_handle_t mrh, mblk_t *mp,
mac_header_info_t *mhip)
{
queue_t *rq = ((softmac_upper_t *)arg)->su_rq;
if (canputnext(rq))
putnext(rq, mp);
else
freemsg(mp);
}
static int
softmac_drv_open(queue_t *rq, dev_t *devp, int flag, int sflag, cred_t *credp)
{
softmac_upper_t *sup = NULL;
softmac_t *softmac;
int err = 0;
if ((err = softmac_hold(*devp, &softmac)) != 0)
return (err);
sup = kmem_cache_alloc(softmac_upper_cachep, KM_NOSLEEP);
if (sup == NULL) {
err = ENOMEM;
goto fail;
}
ASSERT(list_is_empty(&sup->su_req_list));
if ((sup->su_tx_flow_mp = allocb(1, BPRI_HI)) == NULL) {
err = ENOMEM;
goto fail;
}
sup->su_rq = rq;
sup->su_wq = WR(rq);
sup->su_softmac = softmac;
sup->su_mode = SOFTMAC_UNKNOWN;
sup->su_rxinfo.slr_arg = sup;
sup->su_rxinfo.slr_rx = softmac_dedicated_rx;
sup->su_direct_rxinfo.slr_arg = sup;
sup->su_direct_rxinfo.slr_rx = softmac_dedicated_rx;
if ((err = dld_str_open(rq, devp, sup)) != 0) {
freeb(sup->su_tx_flow_mp);
sup->su_tx_flow_mp = NULL;
goto fail;
}
return (0);
fail:
if (sup != NULL)
kmem_cache_free(softmac_upper_cachep, sup);
softmac_rele(softmac);
return (err);
}
static int
softmac_drv_close(queue_t *rq, int flags __unused, cred_t *credp __unused)
{
softmac_upper_t *sup = dld_str_private(rq);
softmac_t *softmac = sup->su_softmac;
ASSERT(WR(rq)->q_next == NULL);
qprocsoff(rq);
ASSERT(sup->su_tx_inprocess == 0);
mutex_enter(&sup->su_disp_mutex);
sup->su_closing = B_TRUE;
while (sup->su_dlpi_pending)
cv_wait(&sup->su_disp_cv, &sup->su_disp_mutex);
mutex_exit(&sup->su_disp_mutex);
softmac_upperstream_close(sup);
if (sup->su_tx_flow_mp != NULL) {
freeb(sup->su_tx_flow_mp);
sup->su_tx_flow_mp = NULL;
}
if (sup->su_active) {
mutex_enter(&softmac->smac_active_mutex);
softmac->smac_nactive--;
mutex_exit(&softmac->smac_active_mutex);
sup->su_active = B_FALSE;
}
sup->su_bound = B_FALSE;
sup->su_softmac = NULL;
sup->su_closing = B_FALSE;
kmem_cache_free(softmac_upper_cachep, sup);
softmac_rele(softmac);
return (dld_str_close(rq));
}
static int
softmac_drv_wput(queue_t *wq, mblk_t *mp)
{
softmac_upper_t *sup = dld_str_private(wq);
t_uscalar_t prim;
ASSERT(wq->q_next == NULL);
switch (DB_TYPE(mp)) {
case M_DATA:
softmac_wput_data(sup, mp);
break;
case M_PROTO:
case M_PCPROTO:
if (MBLKL(mp) < sizeof (t_uscalar_t)) {
freemsg(mp);
return (0);
}
prim = ((union DL_primitives *)mp->b_rptr)->dl_primitive;
if (prim == DL_UNITDATA_REQ) {
softmac_wput_data(sup, mp);
return (0);
}
softmac_wput_nondata(sup, mp);
break;
default:
softmac_wput_nondata(sup, mp);
break;
}
return (0);
}
static int
softmac_drv_wsrv(queue_t *wq)
{
softmac_upper_t *sup = dld_str_private(wq);
ASSERT(wq->q_next == NULL);
mutex_enter(&sup->su_mutex);
if (sup->su_mode != SOFTMAC_FASTPATH) {
sup->su_tx_inprocess++;
mutex_exit(&sup->su_mutex);
(void) dld_wsrv(wq);
mutex_enter(&sup->su_mutex);
if (--sup->su_tx_inprocess == 0)
cv_signal(&sup->su_cv);
} else if (sup->su_tx_busy && SOFTMAC_CANPUTNEXT(sup->su_slp->sl_wq)) {
if (sup->su_tx_notify_func != NULL) {
sup->su_tx_inprocess++;
mutex_exit(&sup->su_mutex);
sup->su_tx_notify_func(sup->su_tx_notify_arg,
(mac_tx_cookie_t)sup);
mutex_enter(&sup->su_mutex);
if (--sup->su_tx_inprocess == 0)
cv_signal(&sup->su_cv);
}
ASSERT(sup->su_tx_flow_mp == NULL);
VERIFY((sup->su_tx_flow_mp = getq(wq)) != NULL);
sup->su_tx_busy = B_FALSE;
}
mutex_exit(&sup->su_mutex);
return (0);
}