#include <sys/types.h>
#include <sys/file.h>
#include <sys/errno.h>
#include <sys/open.h>
#include <sys/cred.h>
#include <sys/uio.h>
#include <sys/stat.h>
#include <sys/ksynch.h>
#include <sys/modctl.h>
#include <sys/conf.h>
#include <sys/devops.h>
#include <sys/debug.h>
#include <sys/cmn_err.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/ds.h>
#include <sys/ds_snmp.h>
#define DS_SNMP_NAME "ds_snmp"
#define DS_SNMP_MAX_OPENS 256
#define DS_BITS_IN_UINT64 64
#define DS_MINOR_POOL_SZ (DS_SNMP_MAX_OPENS / DS_BITS_IN_UINT64)
#define DS_SNMP_MINOR_SHIFT 56
#define DS_SNMP_DBG if (ds_snmp_debug) printf
typedef struct {
uint64_t seq_num;
uint64_t type;
} ds_snmp_msg_t;
typedef enum {
DS_SNMP_REQUEST = 0,
DS_SNMP_REPLY = 1,
DS_SNMP_ERROR = 2
} ds_snmp_msg_type_t;
typedef enum {
DS_SNMP_READY = 0x0,
DS_SNMP_REQUESTED = 0x1,
DS_SNMP_DATA_AVL = 0x2,
DS_SNMP_DATA_ERR = 0x3
} ds_snmp_flags_t;
typedef struct ds_snmp_state {
dev_info_t *dip;
int instance;
dev_t dev;
kmutex_t lock;
kcondvar_t state_cv;
ds_snmp_flags_t state;
void *data;
size_t data_len;
uint64_t req_id;
uint64_t last_req_id;
uint64_t gencount;
boolean_t sc_reset;
} ds_snmp_state_t;
static uint_t ds_snmp_debug = 0;
static void *ds_snmp_statep = NULL;
static int ds_snmp_instance = -1;
static dev_info_t *ds_snmp_devi = NULL;
static kmutex_t ds_snmp_lock;
static kcondvar_t ds_snmp_service_cv;
static int ds_snmp_has_service = B_FALSE;
static ds_svc_hdl_t ds_snmp_handle = DS_INVALID_HDL;
static uint64_t ds_snmp_minor_pool[DS_MINOR_POOL_SZ];
static int ds_snmp_num_opens = 0;
static int ds_snmp_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
static int ds_snmp_attach(dev_info_t *, ddi_attach_cmd_t);
static int ds_snmp_detach(dev_info_t *, ddi_detach_cmd_t);
static int ds_snmp_open(dev_t *, int, int, cred_t *);
static int ds_snmp_close(dev_t, int, int, cred_t *);
static int ds_snmp_read(dev_t, struct uio *, cred_t *);
static int ds_snmp_write(dev_t, struct uio *, cred_t *);
static int ds_snmp_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
static void ds_snmp_reg_handler(ds_cb_arg_t, ds_ver_t *, ds_svc_hdl_t);
static void ds_snmp_unreg_handler(ds_cb_arg_t arg);
static void ds_snmp_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen);
static ds_ver_t ds_snmp_ver_1_0 = { 1, 0 };
static ds_capability_t ds_snmp_cap = {
"snmp",
&ds_snmp_ver_1_0,
1
};
static ds_clnt_ops_t ds_snmp_ops = {
ds_snmp_reg_handler,
ds_snmp_unreg_handler,
ds_snmp_data_handler,
NULL
};
static struct cb_ops ds_snmp_cb_ops = {
ds_snmp_open,
ds_snmp_close,
nodev,
nodev,
nodev,
ds_snmp_read,
ds_snmp_write,
ds_snmp_ioctl,
nodev,
nodev,
nodev,
nochpoll,
ddi_prop_op,
(struct streamtab *)NULL,
D_MP | D_64BIT,
CB_REV,
nodev,
nodev
};
static struct dev_ops ds_snmp_dev_ops = {
DEVO_REV,
0,
ds_snmp_getinfo,
nulldev,
nulldev,
ds_snmp_attach,
ds_snmp_detach,
nodev,
&ds_snmp_cb_ops,
(struct bus_ops *)NULL,
nulldev,
ddi_quiesce_not_needed,
};
static struct modldrv modldrv = {
&mod_driverops,
"Domain Services SNMP Driver",
&ds_snmp_dev_ops
};
static struct modlinkage modlinkage = {
MODREV_1,
(void *)&modldrv,
NULL
};
int
_init(void)
{
int retval;
mutex_init(&ds_snmp_lock, NULL, MUTEX_DRIVER, NULL);
cv_init(&ds_snmp_service_cv, NULL, CV_DRIVER, NULL);
retval = ddi_soft_state_init(&ds_snmp_statep,
sizeof (ds_snmp_state_t), DS_SNMP_MAX_OPENS);
if (retval != 0) {
cv_destroy(&ds_snmp_service_cv);
mutex_destroy(&ds_snmp_lock);
return (retval);
}
retval = mod_install(&modlinkage);
if (retval != 0) {
ddi_soft_state_fini(&ds_snmp_statep);
cv_destroy(&ds_snmp_service_cv);
mutex_destroy(&ds_snmp_lock);
}
return (retval);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
int
_fini(void)
{
int retval;
if ((retval = mod_remove(&modlinkage)) != 0)
return (retval);
ddi_soft_state_fini(&ds_snmp_statep);
cv_destroy(&ds_snmp_service_cv);
mutex_destroy(&ds_snmp_lock);
return (retval);
}
static int
ds_snmp_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
{
ds_snmp_state_t *sp;
int retval = DDI_FAILURE;
ASSERT(resultp != NULL);
switch (cmd) {
case DDI_INFO_DEVT2DEVINFO:
sp = ddi_get_soft_state(ds_snmp_statep, getminor((dev_t)arg));
if (sp != NULL) {
*resultp = sp->dip;
retval = DDI_SUCCESS;
} else
*resultp = NULL;
break;
case DDI_INFO_DEVT2INSTANCE:
*resultp = (void *)(uintptr_t)getminor((dev_t)arg);
retval = DDI_SUCCESS;
break;
}
return (retval);
}
static int
ds_snmp_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
int rv;
switch (cmd) {
case DDI_ATTACH:
if (ds_snmp_instance != -1)
return (DDI_FAILURE);
break;
case DDI_RESUME:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
ds_snmp_instance = ddi_get_instance(dip);
if (ddi_create_minor_node(dip, DS_SNMP_NAME, S_IFCHR, ds_snmp_instance,
DDI_PSEUDO, 0) != DDI_SUCCESS) {
cmn_err(CE_WARN, "%s@%d: Unable to create minor node",
DS_SNMP_NAME, ds_snmp_instance);
return (DDI_FAILURE);
}
bzero(ds_snmp_minor_pool, DS_MINOR_POOL_SZ * sizeof (uint64_t));
ds_snmp_ops.cb_arg = dip;
if ((rv = ds_cap_init(&ds_snmp_cap, &ds_snmp_ops)) != 0) {
cmn_err(CE_NOTE, "ds_cap_init failed: %d", rv);
ddi_remove_minor_node(dip, NULL);
ds_snmp_instance = -1;
return (DDI_FAILURE);
}
ds_snmp_devi = dip;
ddi_report_dev(dip);
return (DDI_SUCCESS);
}
static int
ds_snmp_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
switch (cmd) {
case DDI_DETACH:
if (ds_snmp_instance == -1)
return (DDI_FAILURE);
break;
case DDI_SUSPEND:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
(void) ds_cap_fini(&ds_snmp_cap);
ddi_remove_minor_node(ds_snmp_devi, NULL);
bzero(ds_snmp_minor_pool, DS_MINOR_POOL_SZ * sizeof (uint64_t));
ds_snmp_instance = -1;
ds_snmp_devi = NULL;
return (DDI_SUCCESS);
}
static minor_t
ds_snmp_get_minor(void)
{
uint64_t val;
int i, ndx;
minor_t minor;
mutex_enter(&ds_snmp_lock);
for (ndx = 0; ndx < DS_MINOR_POOL_SZ; ndx++) {
val = ds_snmp_minor_pool[ndx];
for (i = 0; i < DS_BITS_IN_UINT64; i++) {
if ((val & 0x1) == 0) {
ds_snmp_minor_pool[ndx] |= ((uint64_t)1 << i);
ds_snmp_num_opens++;
mutex_exit(&ds_snmp_lock);
minor = ndx * DS_BITS_IN_UINT64 + i + 1;
return (minor);
}
val >>= 1;
}
}
mutex_exit(&ds_snmp_lock);
return (0);
}
static void
ds_snmp_rel_minor(minor_t minor)
{
int i, ndx;
ndx = (minor - 1) / DS_BITS_IN_UINT64;
i = (minor - 1) % DS_BITS_IN_UINT64;
ASSERT(ndx < DS_MINOR_POOL_SZ);
mutex_enter(&ds_snmp_lock);
ds_snmp_num_opens--;
ds_snmp_minor_pool[ndx] &= ~((uint64_t)1 << i);
mutex_exit(&ds_snmp_lock);
}
static boolean_t
ds_snmp_is_open(minor_t minor)
{
uint64_t val;
int i, ndx;
ndx = (minor - 1) / DS_BITS_IN_UINT64;
i = (minor - 1) % DS_BITS_IN_UINT64;
val = ((uint64_t)1 << i);
if (ds_snmp_minor_pool[ndx] & val)
return (B_TRUE);
else
return (B_FALSE);
}
static int
ds_snmp_create_state(dev_t *devp)
{
major_t major;
minor_t minor;
ds_snmp_state_t *sp;
if ((minor = ds_snmp_get_minor()) == 0)
return (EMFILE);
if (ddi_soft_state_zalloc(ds_snmp_statep, minor) != DDI_SUCCESS) {
cmn_err(CE_WARN, "%s@%d: Unable to allocate state",
DS_SNMP_NAME, minor);
ds_snmp_rel_minor(minor);
return (ENOMEM);
}
sp = ddi_get_soft_state(ds_snmp_statep, minor);
if (devp != NULL)
major = getemajor(*devp);
else
major = ddi_driver_major(ds_snmp_devi);
sp->dev = makedevice(major, minor);
if (devp != NULL)
*devp = sp->dev;
sp->instance = minor;
sp->data = NULL;
sp->data_len = 0;
sp->req_id = 0;
sp->last_req_id = 0;
sp->state = DS_SNMP_READY;
sp->sc_reset = B_FALSE;
mutex_init(&sp->lock, NULL, MUTEX_DRIVER, NULL);
cv_init(&sp->state_cv, NULL, CV_DRIVER, NULL);
return (0);
}
static int
ds_snmp_destroy_state(dev_t dev)
{
ds_snmp_state_t *sp;
minor_t minor;
minor = getminor(dev);
if ((sp = ddi_get_soft_state(ds_snmp_statep, minor)) == NULL)
return (ENXIO);
ASSERT(sp->instance == minor);
if (sp->data) {
kmem_free(sp->data, sp->data_len);
}
cv_destroy(&sp->state_cv);
mutex_destroy(&sp->lock);
ddi_soft_state_free(ds_snmp_statep, minor);
ds_snmp_rel_minor(minor);
return (0);
}
static int
ds_snmp_open(dev_t *devp, int flag, int otyp, cred_t *credp)
{
if (otyp != OTYP_CHR)
return (EINVAL);
if (ds_snmp_instance == -1)
return (ENXIO);
mutex_enter(&ds_snmp_lock);
while (ds_snmp_has_service == B_FALSE) {
if (cv_wait_sig(&ds_snmp_service_cv, &ds_snmp_lock) == 0) {
mutex_exit(&ds_snmp_lock);
return (EINTR);
}
}
mutex_exit(&ds_snmp_lock);
return (ds_snmp_create_state(devp));
}
static int
ds_snmp_close(dev_t dev, int flag, int otyp, cred_t *credp)
{
if (otyp != OTYP_CHR)
return (EINVAL);
if (ds_snmp_instance == -1)
return (ENXIO);
if (ds_snmp_handle == DS_INVALID_HDL)
return (EIO);
return (ds_snmp_destroy_state(dev));
}
static int
ds_snmp_read(dev_t dev, struct uio *uiop, cred_t *credp)
{
ds_snmp_state_t *sp;
minor_t minor;
size_t len;
int retval;
caddr_t tmpbufp = (caddr_t)NULL;
mutex_enter(&ds_snmp_lock);
while (ds_snmp_has_service == B_FALSE) {
DS_SNMP_DBG("ds_snmp_read: waiting for service\n");
if (cv_wait_sig(&ds_snmp_service_cv, &ds_snmp_lock) == 0) {
mutex_exit(&ds_snmp_lock);
return (EINTR);
}
}
mutex_exit(&ds_snmp_lock);
if ((len = uiop->uio_resid) == 0)
return (0);
minor = getminor(dev);
if ((sp = ddi_get_soft_state(ds_snmp_statep, minor)) == NULL)
return (ENXIO);
mutex_enter(&sp->lock);
if (sp->sc_reset == B_TRUE) {
mutex_exit(&sp->lock);
return (ECANCELED);
}
if (sp->state != DS_SNMP_DATA_AVL && sp->state != DS_SNMP_DATA_ERR) {
DS_SNMP_DBG("ds_snmp_read: no SNMP data\n");
if (uiop->uio_fmode & (FNDELAY | FNONBLOCK)) {
mutex_exit(&sp->lock);
return (EAGAIN);
}
while (sp->state != DS_SNMP_DATA_AVL &&
sp->state != DS_SNMP_DATA_ERR) {
if (cv_wait_sig(&sp->state_cv, &sp->lock) == 0) {
mutex_exit(&sp->lock);
return (EINTR);
}
}
}
if (sp->state == DS_SNMP_DATA_ERR) {
if (sp->sc_reset == B_TRUE) {
mutex_exit(&sp->lock);
DS_SNMP_DBG("ds_snmp_read: sc got reset, "
"returning ECANCELED\n");
return (ECANCELED);
} else {
sp->state = DS_SNMP_READY;
cv_broadcast(&sp->state_cv);
mutex_exit(&sp->lock);
DS_SNMP_DBG("ds_snmp_read: data error, "
"returning EIO\n");
return (EIO);
}
}
if (len > sp->data_len)
len = sp->data_len;
tmpbufp = kmem_alloc(len, KM_SLEEP);
bcopy(sp->data, (void *)tmpbufp, len);
kmem_free(sp->data, sp->data_len);
sp->data = (caddr_t)NULL;
sp->data_len = 0;
sp->state = DS_SNMP_READY;
cv_broadcast(&sp->state_cv);
mutex_exit(&sp->lock);
retval = uiomove(tmpbufp, len, UIO_READ, uiop);
kmem_free(tmpbufp, len);
return (retval);
}
static int
ds_snmp_write(dev_t dev, struct uio *uiop, cred_t *credp)
{
ds_snmp_state_t *sp;
ds_snmp_msg_t hdr;
minor_t minor;
size_t len;
caddr_t tmpbufp;
size_t orig_size;
mutex_enter(&ds_snmp_lock);
while (ds_snmp_has_service == B_FALSE) {
DS_SNMP_DBG("ds_snmp_write: waiting for service\n");
if (cv_wait_sig(&ds_snmp_service_cv, &ds_snmp_lock) == 0) {
mutex_exit(&ds_snmp_lock);
return (EINTR);
}
}
mutex_exit(&ds_snmp_lock);
minor = getminor(dev);
if ((sp = ddi_get_soft_state(ds_snmp_statep, minor)) == NULL)
return (ENXIO);
orig_size = uiop->uio_resid;
len = uiop->uio_resid + sizeof (ds_snmp_msg_t);
tmpbufp = kmem_alloc(len, KM_SLEEP);
if (uiomove(tmpbufp + sizeof (ds_snmp_msg_t),
len - sizeof (ds_snmp_msg_t), UIO_WRITE, uiop) != 0) {
kmem_free(tmpbufp, len);
return (EIO);
}
mutex_enter(&sp->lock);
if (sp->sc_reset == B_TRUE) {
mutex_exit(&sp->lock);
kmem_free(tmpbufp, len);
DS_SNMP_DBG("ds_snmp_write: sc_reset is TRUE, "
"returning ECANCELD\n");
return (ECANCELED);
}
while (sp->state != DS_SNMP_READY) {
if (cv_wait_sig(&sp->state_cv, &sp->lock) == 0) {
mutex_exit(&sp->lock);
kmem_free(tmpbufp, len);
uiop->uio_resid = orig_size;
return (EINTR);
}
if (sp->state == DS_SNMP_DATA_ERR && sp->sc_reset == B_TRUE) {
DS_SNMP_DBG("ds_snmp_write: woke up with an sc_reset, "
"returning ECANCELED\n");
mutex_exit(&sp->lock);
kmem_free(tmpbufp, len);
return (ECANCELED);
}
}
if (sp->req_id == (((uint64_t)1 << DS_SNMP_MINOR_SHIFT) - 1))
sp->req_id = 0;
hdr.seq_num = ((uint64_t)minor << DS_SNMP_MINOR_SHIFT) | sp->req_id;
sp->last_req_id = hdr.seq_num;
(sp->req_id)++;
sp->state = DS_SNMP_REQUESTED;
mutex_exit(&sp->lock);
hdr.type = DS_SNMP_REQUEST;
bcopy((void *)&hdr, (void *)tmpbufp, sizeof (hdr));
mutex_enter(&ds_snmp_lock);
if (ds_snmp_has_service == B_FALSE) {
DS_SNMP_DBG("ds_snmp_write: service went away, aborting "
"write, returning ECANCELED\n");
mutex_exit(&ds_snmp_lock);
kmem_free(tmpbufp, len);
return (ECANCELED);
}
DS_SNMP_DBG("ds_snmp_write: ds_cap_send(0x%lx, %lu) called.\n",
ds_snmp_handle, len);
(void) ds_cap_send(ds_snmp_handle, tmpbufp, len);
mutex_exit(&ds_snmp_lock);
kmem_free(tmpbufp, len);
return (0);
}
static int
ds_snmp_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
int *rvalp)
{
ds_snmp_state_t *sp;
struct dssnmp_info info;
minor_t minor;
mutex_enter(&ds_snmp_lock);
while (ds_snmp_has_service == B_FALSE) {
DS_SNMP_DBG("ds_snmp_ioctl: waiting for service\n");
if (cv_wait_sig(&ds_snmp_service_cv, &ds_snmp_lock) == 0) {
mutex_exit(&ds_snmp_lock);
return (EINTR);
}
}
mutex_exit(&ds_snmp_lock);
DS_SNMP_DBG("ds_snmp_ioctl: hdl=0x%lx\n", ds_snmp_handle);
minor = getminor(dev);
if ((sp = ddi_get_soft_state(ds_snmp_statep, minor)) == NULL)
return (ENXIO);
if (!(mode & FREAD))
return (EACCES);
switch (cmd) {
case DSSNMP_GETINFO:
mutex_enter(&sp->lock);
if (sp->sc_reset == B_TRUE) {
mutex_exit(&sp->lock);
DS_SNMP_DBG("ds_snmp_ioctl: returning ECANCELED\n");
return (ECANCELED);
}
while (sp->state != DS_SNMP_DATA_AVL &&
sp->state != DS_SNMP_DATA_ERR) {
DS_SNMP_DBG("ds_snmp_ioctl: state=%d, sc_reset=%d, "
"waiting for data\n", sp->state, sp->sc_reset);
if (cv_wait_sig(&sp->state_cv, &sp->lock) == 0) {
sp->state = DS_SNMP_READY;
mutex_exit(&sp->lock);
return (EINTR);
}
}
DS_SNMP_DBG("ds_snmp_ioctl: state=%d, sc_reset=%d, "
"out of wait!\n", sp->state, sp->sc_reset);
if (sp->state == DS_SNMP_DATA_ERR) {
if (sp->sc_reset == B_TRUE) {
mutex_exit(&sp->lock);
DS_SNMP_DBG("ds_snmp_ioctl: sc_reset=TRUE "
"returning ECANCELED\n");
return (ECANCELED);
} else {
sp->state = DS_SNMP_READY;
cv_broadcast(&sp->state_cv);
mutex_exit(&sp->lock);
DS_SNMP_DBG("ds_snmp_ioctl: sc_reset=FALSE "
"returning EIO\n");
return (EIO);
}
}
info.size = sp->data_len;
info.token = sp->gencount;
mutex_exit(&sp->lock);
if (ddi_copyout(&info, (void *)arg, sizeof (info), mode) != 0)
return (EFAULT);
break;
case DSSNMP_CLRLNKRESET:
mutex_enter(&sp->lock);
DS_SNMP_DBG("ds_snmp_ioctl: DSSNMP_CLRLNKRESET\n");
DS_SNMP_DBG("ds_snmp_ioctl: sc_reset=%d\n", sp->sc_reset);
if (sp->sc_reset == B_TRUE) {
if (sp->data) {
DS_SNMP_DBG("ds_snmp_ioctl: data=%p, len=%lu\n",
sp->data, sp->data_len);
kmem_free(sp->data, sp->data_len);
}
sp->data = NULL;
sp->data_len = 0;
sp->state = DS_SNMP_READY;
sp->req_id = 0;
sp->last_req_id = 0;
sp->sc_reset = B_FALSE;
}
mutex_exit(&sp->lock);
break;
default:
return (ENOTTY);
}
return (0);
}
static void
ds_snmp_reg_handler(ds_cb_arg_t arg, ds_ver_t *ver, ds_svc_hdl_t hdl)
{
DS_SNMP_DBG("ds_snmp_reg_handler: registering handle 0x%lx for version "
"0x%x:0x%x\n", (uint64_t)hdl, ver->major, ver->minor);
mutex_enter(&ds_snmp_lock);
ASSERT(ds_snmp_handle == DS_INVALID_HDL);
ds_snmp_handle = hdl;
ds_snmp_has_service = B_TRUE;
cv_broadcast(&ds_snmp_service_cv);
mutex_exit(&ds_snmp_lock);
}
static void
ds_snmp_unreg_handler(ds_cb_arg_t arg)
{
minor_t minor;
ds_snmp_state_t *sp;
DS_SNMP_DBG("ds_snmp_unreg_handler: un-registering ds_snmp service\n");
mutex_enter(&ds_snmp_lock);
if (ds_snmp_num_opens) {
DS_SNMP_DBG("ds_snmp_unreg_handler: %d opens, sc reset!\n",
ds_snmp_num_opens);
for (minor = 1; minor <= DS_SNMP_MAX_OPENS; minor++) {
if (ds_snmp_is_open(minor)) {
DS_SNMP_DBG("ds_snmp_unreg_handler: minor %d "
"open\n", minor);
sp = ddi_get_soft_state(ds_snmp_statep, minor);
if (sp == NULL)
continue;
DS_SNMP_DBG("ds_snmp_unreg_hdlr: about to "
"signal waiters\n");
mutex_enter(&sp->lock);
sp->sc_reset = B_TRUE;
sp->state = DS_SNMP_DATA_ERR;
cv_broadcast(&sp->state_cv);
mutex_exit(&sp->lock);
}
}
}
ds_snmp_handle = DS_INVALID_HDL;
ds_snmp_has_service = B_FALSE;
DS_SNMP_DBG("ds_snmp_unreg_handler: handle invalidated\n");
mutex_exit(&ds_snmp_lock);
}
static void
ds_snmp_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen)
{
ds_snmp_state_t *sp;
ds_snmp_msg_t hdr;
size_t snmp_size;
minor_t minor;
if (buflen < sizeof (hdr)) {
cmn_err(CE_WARN,
"ds_snmp_data_handler: buflen <%lu> too small", buflen);
return;
}
ASSERT(buf != NULL);
bcopy(buf, (void *)&hdr, sizeof (hdr));
DS_SNMP_DBG("ds_snmp_data_handler: msg buf len 0x%lx : type 0x%lx, "
"seqn 0x%lx\n", buflen, hdr.type, hdr.seq_num);
minor = (int)(hdr.seq_num >> DS_SNMP_MINOR_SHIFT);
if ((sp = ddi_get_soft_state(ds_snmp_statep, minor)) == NULL)
return;
mutex_enter(&sp->lock);
if (sp->state != DS_SNMP_REQUESTED) {
DS_SNMP_DBG("Received SNMP data without request");
mutex_exit(&sp->lock);
return;
}
ASSERT(sp->data_len == 0);
ASSERT(sp->data == NULL);
if (hdr.seq_num != sp->last_req_id) {
cmn_err(CE_WARN, "Received DS snmp data out of sequence with "
"request");
mutex_exit(&sp->lock);
return;
}
if (hdr.type == DS_SNMP_ERROR) {
sp->state = DS_SNMP_DATA_ERR;
DS_SNMP_DBG("ds_snmp_data_handler: hdr.type = DS_SNMP_ERROR\n");
} else {
snmp_size = buflen - sizeof (ds_snmp_msg_t);
sp->data = kmem_alloc(snmp_size, KM_SLEEP);
sp->data_len = snmp_size;
sp->state = DS_SNMP_DATA_AVL;
bcopy((caddr_t)buf + sizeof (ds_snmp_msg_t),
sp->data, sp->data_len);
}
sp->gencount++;
cv_broadcast(&sp->state_cv);
mutex_exit(&sp->lock);
}