#include <sys/types.h>
#include <sys/conf.h>
#include <sys/modctl.h>
#include <sys/cmn_err.h>
#include <sys/sunddi.h>
#include <sys/esunddi.h>
#include <sys/strsubr.h>
#include <sys/ddi.h>
#include <sys/dlpi.h>
#include <sys/strsun.h>
#include <sys/policy.h>
static struct streamtab drstab;
static struct fmodsw fsw = {
DRMODNAME,
&drstab,
D_MP
};
static struct modlstrmod modlstrmod = {
&mod_strmodops, "dr compatibility for DLPI style 2 drivers", &fsw
};
static struct modlinkage modlinkage = {
MODREV_1, &modlstrmod, 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));
}
static int dropen(queue_t *, dev_t *, int, int, cred_t *);
static int drclose(queue_t *, int, cred_t *);
static int drrput(queue_t *, mblk_t *);
static int drwput(queue_t *, mblk_t *);
static struct module_info drinfo = {
0,
DRMODNAME,
0,
INFPSZ,
1,
0
};
static struct qinit drrinit = {
(int (*)())drrput,
NULL,
dropen,
drclose,
NULL,
&drinfo
};
static struct qinit drwinit = {
(int (*)())drwput,
NULL,
NULL,
NULL,
NULL,
&drinfo
};
static struct streamtab drstab = {
&drrinit,
&drwinit,
NULL,
NULL
};
#define MAX_DLREQS 64
#define INCR(x) {(x)++; if ((x) >= MAX_DLREQS) (x) = 0; }
struct drstate {
kmutex_t dr_lock;
major_t dr_major;
int dr_nfirst;
int dr_nlast;
dev_info_t *dr_dip[MAX_DLREQS];
};
static int
dropen(queue_t *q, dev_t *devp, int oflag, int sflag, cred_t *crp)
{
struct drstate *dsp;
if (sflag != MODOPEN) {
return (EINVAL);
}
if (secpolicy_net_rawaccess(crp) != 0) {
return (EPERM);
}
if (q->q_ptr != NULL) {
return (0);
}
dsp = kmem_zalloc(sizeof (*dsp), KM_SLEEP);
dsp->dr_major = getmajor(*devp);
mutex_init(&dsp->dr_lock, NULL, MUTEX_DEFAULT, NULL);
q->q_ptr = OTHERQ(q)->q_ptr = dsp;
qprocson(q);
ddi_assoc_queue_with_devi(q, NULL);
return (0);
}
static int
drclose(queue_t *q, int cflag, cred_t *crp)
{
struct drstate *dsp = q->q_ptr;
ASSERT(dsp);
ddi_assoc_queue_with_devi(q, NULL);
qprocsoff(q);
mutex_destroy(&dsp->dr_lock);
kmem_free(dsp, sizeof (*dsp));
q->q_ptr = NULL;
return (0);
}
static int
drrput(queue_t *q, mblk_t *mp)
{
struct drstate *dsp;
union DL_primitives *dlp;
dev_info_t *dip;
switch (DB_TYPE(mp)) {
case M_PROTO:
case M_PCPROTO:
break;
default:
putnext(q, mp);
return (0);
}
if (MBLKL(mp) < sizeof (t_uscalar_t)) {
putnext(q, mp);
return (0);
}
dlp = (union DL_primitives *)mp->b_rptr;
switch (dlp->dl_primitive) {
case DL_OK_ACK: {
if (MBLKL(mp) < DL_OK_ACK_SIZE) {
putnext(q, mp);
return (0);
}
dsp = q->q_ptr;
switch (dlp->ok_ack.dl_correct_primitive) {
case DL_ATTACH_REQ:
mutex_enter(&dsp->dr_lock);
dip = dsp->dr_dip[dsp->dr_nlast];
dsp->dr_dip[dsp->dr_nlast] = NULL;
INCR(dsp->dr_nlast);
mutex_exit(&dsp->dr_lock);
if (dip) {
ddi_assoc_queue_with_devi(q, dip);
ddi_release_devi(dip);
}
break;
case DL_DETACH_REQ:
ddi_assoc_queue_with_devi(q, NULL);
break;
default:
break;
}
break;
}
case DL_ERROR_ACK:
if (dlp->error_ack.dl_error_primitive != DL_ATTACH_REQ)
break;
dsp = q->q_ptr;
mutex_enter(&dsp->dr_lock);
dip = dsp->dr_dip[dsp->dr_nlast];
dsp->dr_dip[dsp->dr_nlast] = NULL;
INCR(dsp->dr_nlast);
mutex_exit(&dsp->dr_lock);
if (dip) {
ddi_release_devi(dip);
}
break;
default:
break;
}
putnext(q, mp);
return (0);
}
static int
drwput(queue_t *q, mblk_t *mp)
{
struct drstate *dsp;
union DL_primitives *dlp;
dev_info_t *dip;
switch (DB_TYPE(mp)) {
case M_PROTO:
case M_PCPROTO:
break;
default:
putnext(q, mp);
return (0);
}
if (MBLKL(mp) < sizeof (t_uscalar_t)) {
putnext(q, mp);
return (0);
}
dlp = (union DL_primitives *)mp->b_rptr;
switch (dlp->dl_primitive) {
case DL_ATTACH_REQ:
dip = NULL;
dsp = q->q_ptr;
if (MBLKL(mp) >= DL_OK_ACK_SIZE) {
dip = ddi_hold_devi_by_instance(dsp->dr_major,
dlp->attach_req.dl_ppa, E_DDI_HOLD_DEVI_NOATTACH);
}
mutex_enter(&dsp->dr_lock);
dsp->dr_dip[dsp->dr_nfirst] = dip;
INCR(dsp->dr_nfirst);
ASSERT(dsp->dr_nfirst != dsp->dr_nlast);
if (dsp->dr_nfirst == dsp->dr_nlast) {
cmn_err(CE_WARN, "drcompat: internal buffer full");
}
mutex_exit(&dsp->dr_lock);
break;
default:
break;
}
putnext(q, mp);
return (0);
}