xref: /illumos-gate/usr/src/uts/sun4v/io/ds_snmp.c (revision c659a048)
10d63ce2bSvenki /*
20d63ce2bSvenki  * CDDL HEADER START
30d63ce2bSvenki  *
40d63ce2bSvenki  * The contents of this file are subject to the terms of the
50d63ce2bSvenki  * Common Development and Distribution License (the "License").
60d63ce2bSvenki  * You may not use this file except in compliance with the License.
70d63ce2bSvenki  *
80d63ce2bSvenki  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90d63ce2bSvenki  * or http://www.opensolaris.org/os/licensing.
100d63ce2bSvenki  * See the License for the specific language governing permissions
110d63ce2bSvenki  * and limitations under the License.
120d63ce2bSvenki  *
130d63ce2bSvenki  * When distributing Covered Code, include this CDDL HEADER in each
140d63ce2bSvenki  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150d63ce2bSvenki  * If applicable, add the following below this CDDL HEADER, with the
160d63ce2bSvenki  * fields enclosed by brackets "[]" replaced with your own identifying
170d63ce2bSvenki  * information: Portions Copyright [yyyy] [name of copyright owner]
180d63ce2bSvenki  *
190d63ce2bSvenki  * CDDL HEADER END
200d63ce2bSvenki  */
210d63ce2bSvenki /*
22*c659a048SMichael Bergknoff  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
230d63ce2bSvenki  */
240d63ce2bSvenki 
250d63ce2bSvenki 
260d63ce2bSvenki /*
270d63ce2bSvenki  * sun4v domain services SNMP driver
280d63ce2bSvenki  */
290d63ce2bSvenki 
300d63ce2bSvenki #include <sys/types.h>
310d63ce2bSvenki #include <sys/file.h>
320d63ce2bSvenki #include <sys/errno.h>
330d63ce2bSvenki #include <sys/open.h>
340d63ce2bSvenki #include <sys/cred.h>
350d63ce2bSvenki #include <sys/uio.h>
360d63ce2bSvenki #include <sys/stat.h>
370d63ce2bSvenki #include <sys/ksynch.h>
380d63ce2bSvenki #include <sys/modctl.h>
390d63ce2bSvenki #include <sys/conf.h>
400d63ce2bSvenki #include <sys/devops.h>
410d63ce2bSvenki #include <sys/debug.h>
420d63ce2bSvenki #include <sys/cmn_err.h>
430d63ce2bSvenki #include <sys/ddi.h>
440d63ce2bSvenki #include <sys/sunddi.h>
450d63ce2bSvenki #include <sys/ds.h>
460d63ce2bSvenki #include <sys/ds_snmp.h>
470d63ce2bSvenki 
480d63ce2bSvenki #define	DS_SNMP_NAME		"ds_snmp"
490d63ce2bSvenki #define	DS_SNMP_MAX_OPENS	256
500d63ce2bSvenki #define	DS_BITS_IN_UINT64	64
510d63ce2bSvenki #define	DS_MINOR_POOL_SZ	(DS_SNMP_MAX_OPENS / DS_BITS_IN_UINT64)
520d63ce2bSvenki #define	DS_SNMP_MINOR_SHIFT	56
530d63ce2bSvenki #define	DS_SNMP_DBG		if (ds_snmp_debug) printf
540d63ce2bSvenki 
550d63ce2bSvenki typedef	struct {
560d63ce2bSvenki 	uint64_t	seq_num;
570d63ce2bSvenki 	uint64_t	type;
580d63ce2bSvenki } ds_snmp_msg_t;
590d63ce2bSvenki 
600d63ce2bSvenki typedef	enum {
610d63ce2bSvenki 	DS_SNMP_REQUEST	= 0,
620d63ce2bSvenki 	DS_SNMP_REPLY	= 1,
630d63ce2bSvenki 	DS_SNMP_ERROR = 2
640d63ce2bSvenki } ds_snmp_msg_type_t;
650d63ce2bSvenki 
660d63ce2bSvenki typedef enum {
670d63ce2bSvenki 	DS_SNMP_READY = 0x0,
680d63ce2bSvenki 	DS_SNMP_REQUESTED = 0x1,
690d63ce2bSvenki 	DS_SNMP_DATA_AVL = 0x2,
700d63ce2bSvenki 	DS_SNMP_DATA_ERR = 0x3
710d63ce2bSvenki } ds_snmp_flags_t;
720d63ce2bSvenki 
730d63ce2bSvenki /*
740d63ce2bSvenki  * The single mutex 'lock' protects all the SNMP/DS variables in the state
750d63ce2bSvenki  * structure.
760d63ce2bSvenki  *
770d63ce2bSvenki  * The condition variable 'state_cv' helps serialize write() calls for a
780d63ce2bSvenki  * single descriptor. When write() is called, it sets a flag to indicate
790d63ce2bSvenki  * that an SNMP request has been made to the agent. No more write()'s on
800d63ce2bSvenki  * the same open descriptor will be allowed until this flag is cleared via
810d63ce2bSvenki  * a matching read(), where the requested packet is consumed on arrival.
820d63ce2bSvenki  * Read() then wakes up any waiters blocked in write() for sending the next
830d63ce2bSvenki  * SNMP request to the agent.
840d63ce2bSvenki  */
850d63ce2bSvenki typedef struct ds_snmp_state {
860d63ce2bSvenki 	dev_info_t	*dip;
870d63ce2bSvenki 	int		instance;
880d63ce2bSvenki 	dev_t		dev;
890d63ce2bSvenki 
900d63ce2bSvenki 	/* SNMP/DS */
910d63ce2bSvenki 	kmutex_t	lock;
920d63ce2bSvenki 	kcondvar_t	state_cv;
930d63ce2bSvenki 	ds_snmp_flags_t	state;
940d63ce2bSvenki 	void		*data;
950d63ce2bSvenki 	size_t		data_len;
960d63ce2bSvenki 	uint64_t	req_id;
970d63ce2bSvenki 	uint64_t	last_req_id;
980d63ce2bSvenki 	uint64_t	gencount;
990d63ce2bSvenki 	boolean_t	sc_reset;
1000d63ce2bSvenki } ds_snmp_state_t;
1010d63ce2bSvenki 
1020d63ce2bSvenki 
1030d63ce2bSvenki static uint_t		ds_snmp_debug = 0;
1040d63ce2bSvenki static void		*ds_snmp_statep = NULL;
1050d63ce2bSvenki static int		ds_snmp_instance = -1;
1060d63ce2bSvenki static dev_info_t	*ds_snmp_devi = NULL;
1070d63ce2bSvenki 
1080d63ce2bSvenki /*
1090d63ce2bSvenki  * The ds_snmp_lock mutex protects the following data global to the
1100d63ce2bSvenki  * driver.
1110d63ce2bSvenki  *
1120d63ce2bSvenki  * The ds_snmp_service_cv condition variable is used to resolve the
1130d63ce2bSvenki  * potential race between the registration of snmp service via a
1140d63ce2bSvenki  * ds_cap_init() in attach(), the acknowledgement of this registration
1150d63ce2bSvenki  * at a later time in ds_snmp_reg_handler(), and a possible open() at
1160d63ce2bSvenki  * a time inbetween. The ds_snmp_has_service and ds_snmp_handle are
1170d63ce2bSvenki  * used to indicate whether the registration acknowledgement has happened
1180d63ce2bSvenki  * or not.
1190d63ce2bSvenki  *
1200d63ce2bSvenki  * The ds_snmp_minor_pool[] is a bitmask to allocate and keep track of
1210d63ce2bSvenki  * minor numbers dynamically.
1220d63ce2bSvenki  */
1230d63ce2bSvenki static kmutex_t		ds_snmp_lock;
1240d63ce2bSvenki static kcondvar_t	ds_snmp_service_cv;
1250d63ce2bSvenki static int		ds_snmp_has_service = B_FALSE;
1260d63ce2bSvenki static ds_svc_hdl_t	ds_snmp_handle = DS_INVALID_HDL;
1270d63ce2bSvenki static uint64_t		ds_snmp_minor_pool[DS_MINOR_POOL_SZ];	/* bitmask */
1280d63ce2bSvenki static int		ds_snmp_num_opens = 0;
1290d63ce2bSvenki 
1300d63ce2bSvenki static int ds_snmp_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
1310d63ce2bSvenki static int ds_snmp_attach(dev_info_t *, ddi_attach_cmd_t);
1320d63ce2bSvenki static int ds_snmp_detach(dev_info_t *, ddi_detach_cmd_t);
1330d63ce2bSvenki static int ds_snmp_open(dev_t *, int, int, cred_t *);
1340d63ce2bSvenki static int ds_snmp_close(dev_t, int, int, cred_t *);
1350d63ce2bSvenki static int ds_snmp_read(dev_t, struct uio *, cred_t *);
1360d63ce2bSvenki static int ds_snmp_write(dev_t, struct uio *, cred_t *);
1370d63ce2bSvenki static int ds_snmp_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
1380d63ce2bSvenki 
1390d63ce2bSvenki /*
1400d63ce2bSvenki  * DS Callbacks
1410d63ce2bSvenki  */
1420d63ce2bSvenki static void ds_snmp_reg_handler(ds_cb_arg_t, ds_ver_t *, ds_svc_hdl_t);
1430d63ce2bSvenki static void ds_snmp_unreg_handler(ds_cb_arg_t arg);
1440d63ce2bSvenki static void ds_snmp_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen);
1450d63ce2bSvenki 
1460d63ce2bSvenki /*
1470d63ce2bSvenki  * SNMP DS capability registration
1480d63ce2bSvenki  */
1490d63ce2bSvenki static ds_ver_t ds_snmp_ver_1_0 = { 1, 0 };
1500d63ce2bSvenki static ds_capability_t ds_snmp_cap = {
1510d63ce2bSvenki 	"snmp",
1520d63ce2bSvenki 	&ds_snmp_ver_1_0,
1530d63ce2bSvenki 	1
1540d63ce2bSvenki };
1550d63ce2bSvenki 
1560d63ce2bSvenki /*
1570d63ce2bSvenki  * SNMP DS Client callback vector
1580d63ce2bSvenki  */
1590d63ce2bSvenki static ds_clnt_ops_t ds_snmp_ops = {
1600d63ce2bSvenki 	ds_snmp_reg_handler,	/* ds_reg_cb */
1610d63ce2bSvenki 	ds_snmp_unreg_handler,	/* ds_unreg_cb */
1620d63ce2bSvenki 	ds_snmp_data_handler,	/* ds_data_cb */
1630d63ce2bSvenki 	NULL			/* cb_arg */
1640d63ce2bSvenki };
1650d63ce2bSvenki 
1660d63ce2bSvenki /*
1670d63ce2bSvenki  * DS SNMP driver Ops Vector
1680d63ce2bSvenki  */
1690d63ce2bSvenki static struct cb_ops ds_snmp_cb_ops = {
1700d63ce2bSvenki 	ds_snmp_open,		/* cb_open */
1710d63ce2bSvenki 	ds_snmp_close,		/* cb_close */
1720d63ce2bSvenki 	nodev,			/* cb_strategy */
1730d63ce2bSvenki 	nodev,			/* cb_print */
1740d63ce2bSvenki 	nodev,			/* cb_dump */
1750d63ce2bSvenki 	ds_snmp_read,		/* cb_read */
1760d63ce2bSvenki 	ds_snmp_write,		/* cb_write */
1770d63ce2bSvenki 	ds_snmp_ioctl,		/* cb_ioctl */
1780d63ce2bSvenki 	nodev,			/* cb_devmap */
1790d63ce2bSvenki 	nodev,			/* cb_mmap */
1800d63ce2bSvenki 	nodev,			/* cb_segmap */
1810d63ce2bSvenki 	nochpoll,		/* cb_chpoll */
1820d63ce2bSvenki 	ddi_prop_op,		/* cb_prop_op */
1830d63ce2bSvenki 	(struct streamtab *)NULL, /* cb_str */
1840d63ce2bSvenki 	D_MP | D_64BIT,		/* cb_flag */
1850d63ce2bSvenki 	CB_REV,			/* cb_rev */
1860d63ce2bSvenki 	nodev,			/* cb_aread */
1870d63ce2bSvenki 	nodev			/* cb_awrite */
1880d63ce2bSvenki };
1890d63ce2bSvenki 
1900d63ce2bSvenki static struct dev_ops ds_snmp_dev_ops = {
1910d63ce2bSvenki 	DEVO_REV,		/* devo_rev */
1920d63ce2bSvenki 	0,			/* devo_refcnt */
1930d63ce2bSvenki 	ds_snmp_getinfo,	/* devo_getinfo */
1940d63ce2bSvenki 	nulldev,		/* devo_identify */
1950d63ce2bSvenki 	nulldev,		/* devo_probe */
1960d63ce2bSvenki 	ds_snmp_attach,		/* devo_attach */
1970d63ce2bSvenki 	ds_snmp_detach,		/* devo_detach */
1980d63ce2bSvenki 	nodev,			/* devo_reset */
1990d63ce2bSvenki 	&ds_snmp_cb_ops,	/* devo_cb_ops */
2000d63ce2bSvenki 	(struct bus_ops *)NULL,	/* devo_bus_ops */
20119397407SSherry Moore 	nulldev,		/* devo_power */
20219397407SSherry Moore 	ddi_quiesce_not_needed,		/* devo_quiesce */
2030d63ce2bSvenki };
2040d63ce2bSvenki 
2050d63ce2bSvenki static struct modldrv modldrv = {
2060d63ce2bSvenki 	&mod_driverops,
20719397407SSherry Moore 	"Domain Services SNMP Driver",
2080d63ce2bSvenki 	&ds_snmp_dev_ops
2090d63ce2bSvenki };
2100d63ce2bSvenki 
2110d63ce2bSvenki static struct modlinkage modlinkage = {
2120d63ce2bSvenki 	MODREV_1,
2130d63ce2bSvenki 	(void *)&modldrv,
2140d63ce2bSvenki 	NULL
2150d63ce2bSvenki };
2160d63ce2bSvenki 
2170d63ce2bSvenki int
_init(void)2180d63ce2bSvenki _init(void)
2190d63ce2bSvenki {
2200d63ce2bSvenki 	int retval;
2210d63ce2bSvenki 
2220d63ce2bSvenki 	mutex_init(&ds_snmp_lock, NULL, MUTEX_DRIVER, NULL);
2230d63ce2bSvenki 	cv_init(&ds_snmp_service_cv, NULL, CV_DRIVER, NULL);
2240d63ce2bSvenki 
2250d63ce2bSvenki 	retval = ddi_soft_state_init(&ds_snmp_statep,
2260d63ce2bSvenki 	    sizeof (ds_snmp_state_t), DS_SNMP_MAX_OPENS);
2270d63ce2bSvenki 	if (retval != 0) {
2280d63ce2bSvenki 		cv_destroy(&ds_snmp_service_cv);
2290d63ce2bSvenki 		mutex_destroy(&ds_snmp_lock);
2300d63ce2bSvenki 		return (retval);
2310d63ce2bSvenki 	}
2320d63ce2bSvenki 
2330d63ce2bSvenki 	retval = mod_install(&modlinkage);
2340d63ce2bSvenki 	if (retval != 0) {
2350d63ce2bSvenki 		ddi_soft_state_fini(&ds_snmp_statep);
2360d63ce2bSvenki 		cv_destroy(&ds_snmp_service_cv);
2370d63ce2bSvenki 		mutex_destroy(&ds_snmp_lock);
2380d63ce2bSvenki 	}
2390d63ce2bSvenki 
2400d63ce2bSvenki 	return (retval);
2410d63ce2bSvenki }
2420d63ce2bSvenki 
2430d63ce2bSvenki int
_info(struct modinfo * modinfop)2440d63ce2bSvenki _info(struct modinfo *modinfop)
2450d63ce2bSvenki {
2460d63ce2bSvenki 	return (mod_info(&modlinkage, modinfop));
2470d63ce2bSvenki }
2480d63ce2bSvenki 
2490d63ce2bSvenki int
_fini(void)2500d63ce2bSvenki _fini(void)
2510d63ce2bSvenki {
2520d63ce2bSvenki 	int retval;
2530d63ce2bSvenki 
2540d63ce2bSvenki 	if ((retval = mod_remove(&modlinkage)) != 0)
2550d63ce2bSvenki 		return (retval);
2560d63ce2bSvenki 
2570d63ce2bSvenki 	ddi_soft_state_fini(&ds_snmp_statep);
2580d63ce2bSvenki 
2590d63ce2bSvenki 	cv_destroy(&ds_snmp_service_cv);
2600d63ce2bSvenki 	mutex_destroy(&ds_snmp_lock);
2610d63ce2bSvenki 
2620d63ce2bSvenki 	return (retval);
2630d63ce2bSvenki }
2640d63ce2bSvenki 
2650d63ce2bSvenki /*ARGSUSED*/
2660d63ce2bSvenki static int
ds_snmp_getinfo(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** resultp)2670d63ce2bSvenki ds_snmp_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
2680d63ce2bSvenki {
2690d63ce2bSvenki 	ds_snmp_state_t *sp;
2700d63ce2bSvenki 	int retval = DDI_FAILURE;
2710d63ce2bSvenki 
2720d63ce2bSvenki 	ASSERT(resultp != NULL);
2730d63ce2bSvenki 
2740d63ce2bSvenki 	switch (cmd) {
2750d63ce2bSvenki 	case DDI_INFO_DEVT2DEVINFO:
2760d63ce2bSvenki 		sp = ddi_get_soft_state(ds_snmp_statep, getminor((dev_t)arg));
2770d63ce2bSvenki 		if (sp != NULL) {
2780d63ce2bSvenki 			*resultp = sp->dip;
2790d63ce2bSvenki 			retval = DDI_SUCCESS;
2800d63ce2bSvenki 		} else
2810d63ce2bSvenki 			*resultp = NULL;
2820d63ce2bSvenki 		break;
2830d63ce2bSvenki 
2840d63ce2bSvenki 	case DDI_INFO_DEVT2INSTANCE:
2850d63ce2bSvenki 		*resultp = (void *)(uintptr_t)getminor((dev_t)arg);
2860d63ce2bSvenki 		retval = DDI_SUCCESS;
2870d63ce2bSvenki 		break;
2880d63ce2bSvenki 	}
2890d63ce2bSvenki 
2900d63ce2bSvenki 	return (retval);
2910d63ce2bSvenki }
2920d63ce2bSvenki 
2930d63ce2bSvenki static int
ds_snmp_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)2940d63ce2bSvenki ds_snmp_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
2950d63ce2bSvenki {
2960d63ce2bSvenki 	int	rv;
2970d63ce2bSvenki 
2980d63ce2bSvenki 	switch (cmd) {
2990d63ce2bSvenki 	case DDI_ATTACH:
3000d63ce2bSvenki 		if (ds_snmp_instance != -1)
3010d63ce2bSvenki 			return (DDI_FAILURE);
3020d63ce2bSvenki 		break;
3030d63ce2bSvenki 
3040d63ce2bSvenki 	case DDI_RESUME:
3050d63ce2bSvenki 		return (DDI_SUCCESS);
3060d63ce2bSvenki 
3070d63ce2bSvenki 	default:
3080d63ce2bSvenki 		return (DDI_FAILURE);
3090d63ce2bSvenki 	}
3100d63ce2bSvenki 
3110d63ce2bSvenki 	ds_snmp_instance = ddi_get_instance(dip);
3120d63ce2bSvenki 	if (ddi_create_minor_node(dip, DS_SNMP_NAME, S_IFCHR, ds_snmp_instance,
313ece6eed9Sfw 	    DDI_PSEUDO, 0) != DDI_SUCCESS) {
3140d63ce2bSvenki 		cmn_err(CE_WARN, "%s@%d: Unable to create minor node",
3150d63ce2bSvenki 		    DS_SNMP_NAME, ds_snmp_instance);
3160d63ce2bSvenki 		return (DDI_FAILURE);
3170d63ce2bSvenki 	}
3180d63ce2bSvenki 
3190d63ce2bSvenki 	bzero(ds_snmp_minor_pool, DS_MINOR_POOL_SZ * sizeof (uint64_t));
3200d63ce2bSvenki 
3210d63ce2bSvenki 	ds_snmp_ops.cb_arg = dip;
3220d63ce2bSvenki 	if ((rv = ds_cap_init(&ds_snmp_cap, &ds_snmp_ops)) != 0) {
3230d63ce2bSvenki 		cmn_err(CE_NOTE, "ds_cap_init failed: %d", rv);
3240d63ce2bSvenki 		ddi_remove_minor_node(dip, NULL);
3250d63ce2bSvenki 		ds_snmp_instance = -1;
3260d63ce2bSvenki 		return (DDI_FAILURE);
3270d63ce2bSvenki 	}
3280d63ce2bSvenki 
3290d63ce2bSvenki 	ds_snmp_devi = dip;
3300d63ce2bSvenki 	ddi_report_dev(dip);
3310d63ce2bSvenki 
3320d63ce2bSvenki 	return (DDI_SUCCESS);
3330d63ce2bSvenki }
3340d63ce2bSvenki 
3350d63ce2bSvenki /*ARGSUSED*/
3360d63ce2bSvenki static int
ds_snmp_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)3370d63ce2bSvenki ds_snmp_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
3380d63ce2bSvenki {
3390d63ce2bSvenki 	switch (cmd) {
3400d63ce2bSvenki 	case DDI_DETACH:
3410d63ce2bSvenki 		if (ds_snmp_instance == -1)
3420d63ce2bSvenki 			return (DDI_FAILURE);
3430d63ce2bSvenki 		break;
3440d63ce2bSvenki 
3450d63ce2bSvenki 	case DDI_SUSPEND:
3460d63ce2bSvenki 		return (DDI_SUCCESS);
3470d63ce2bSvenki 
3480d63ce2bSvenki 	default:
3490d63ce2bSvenki 		return (DDI_FAILURE);
3500d63ce2bSvenki 	}
3510d63ce2bSvenki 
3520d63ce2bSvenki 	(void) ds_cap_fini(&ds_snmp_cap);
3530d63ce2bSvenki 
3540d63ce2bSvenki 	ddi_remove_minor_node(ds_snmp_devi, NULL);
3550d63ce2bSvenki 	bzero(ds_snmp_minor_pool, DS_MINOR_POOL_SZ * sizeof (uint64_t));
3560d63ce2bSvenki 
3570d63ce2bSvenki 	ds_snmp_instance = -1;
3580d63ce2bSvenki 	ds_snmp_devi = NULL;
3590d63ce2bSvenki 
3600d63ce2bSvenki 	return (DDI_SUCCESS);
3610d63ce2bSvenki }
3620d63ce2bSvenki 
3630d63ce2bSvenki static minor_t
ds_snmp_get_minor(void)3640d63ce2bSvenki ds_snmp_get_minor(void)
3650d63ce2bSvenki {
3660d63ce2bSvenki 	uint64_t	val;
3670d63ce2bSvenki 	int		i, ndx;
3680d63ce2bSvenki 	minor_t		minor;
3690d63ce2bSvenki 
3700d63ce2bSvenki 	mutex_enter(&ds_snmp_lock);
3710d63ce2bSvenki 	for (ndx = 0; ndx < DS_MINOR_POOL_SZ; ndx++) {
3720d63ce2bSvenki 		val = ds_snmp_minor_pool[ndx];
3730d63ce2bSvenki 		for (i = 0; i < DS_BITS_IN_UINT64; i++) {
3740d63ce2bSvenki 			if ((val & 0x1) == 0) {
3750d63ce2bSvenki 				ds_snmp_minor_pool[ndx] |= ((uint64_t)1 << i);
3760d63ce2bSvenki 				ds_snmp_num_opens++;
3770d63ce2bSvenki 				mutex_exit(&ds_snmp_lock);
3780d63ce2bSvenki 
3790d63ce2bSvenki 				minor = ndx * DS_BITS_IN_UINT64 + i + 1;
3800d63ce2bSvenki 
3810d63ce2bSvenki 				return (minor);
3820d63ce2bSvenki 			}
3830d63ce2bSvenki 			val >>= 1;
3840d63ce2bSvenki 		}
3850d63ce2bSvenki 	}
3860d63ce2bSvenki 	mutex_exit(&ds_snmp_lock);
3870d63ce2bSvenki 
3880d63ce2bSvenki 	return (0);
3890d63ce2bSvenki }
3900d63ce2bSvenki 
3910d63ce2bSvenki static void
ds_snmp_rel_minor(minor_t minor)3920d63ce2bSvenki ds_snmp_rel_minor(minor_t minor)
3930d63ce2bSvenki {
3940d63ce2bSvenki 	int	i, ndx;
3950d63ce2bSvenki 
3960d63ce2bSvenki 	ndx = (minor - 1) / DS_BITS_IN_UINT64;
3970d63ce2bSvenki 	i = (minor - 1) % DS_BITS_IN_UINT64;
3980d63ce2bSvenki 
3990d63ce2bSvenki 	ASSERT(ndx < DS_MINOR_POOL_SZ);
4000d63ce2bSvenki 
4010d63ce2bSvenki 	mutex_enter(&ds_snmp_lock);
4020d63ce2bSvenki 
4030d63ce2bSvenki 	ds_snmp_num_opens--;
4040d63ce2bSvenki 	ds_snmp_minor_pool[ndx] &= ~((uint64_t)1 << i);
4050d63ce2bSvenki 
4060d63ce2bSvenki 	mutex_exit(&ds_snmp_lock);
4070d63ce2bSvenki }
4080d63ce2bSvenki 
4090d63ce2bSvenki static boolean_t
ds_snmp_is_open(minor_t minor)4100d63ce2bSvenki ds_snmp_is_open(minor_t minor)
4110d63ce2bSvenki {
4120d63ce2bSvenki 	uint64_t	val;
4130d63ce2bSvenki 	int		i, ndx;
4140d63ce2bSvenki 
4150d63ce2bSvenki 	ndx = (minor - 1) / DS_BITS_IN_UINT64;
4160d63ce2bSvenki 	i = (minor - 1) % DS_BITS_IN_UINT64;
4170d63ce2bSvenki 
4180d63ce2bSvenki 	val = ((uint64_t)1 << i);
4190d63ce2bSvenki 	if (ds_snmp_minor_pool[ndx] & val)
4200d63ce2bSvenki 		return (B_TRUE);
4210d63ce2bSvenki 	else
4220d63ce2bSvenki 		return (B_FALSE);
4230d63ce2bSvenki }
4240d63ce2bSvenki 
4250d63ce2bSvenki static int
ds_snmp_create_state(dev_t * devp)4260d63ce2bSvenki ds_snmp_create_state(dev_t *devp)
4270d63ce2bSvenki {
4280d63ce2bSvenki 	major_t	major;
4290d63ce2bSvenki 	minor_t	minor;
4300d63ce2bSvenki 	ds_snmp_state_t	*sp;
4310d63ce2bSvenki 
4320d63ce2bSvenki 	if ((minor = ds_snmp_get_minor()) == 0)
4330d63ce2bSvenki 		return (EMFILE);
4340d63ce2bSvenki 
4350d63ce2bSvenki 	if (ddi_soft_state_zalloc(ds_snmp_statep, minor) != DDI_SUCCESS) {
4360d63ce2bSvenki 		cmn_err(CE_WARN, "%s@%d: Unable to allocate state",
4370d63ce2bSvenki 		    DS_SNMP_NAME, minor);
4380d63ce2bSvenki 		ds_snmp_rel_minor(minor);
4390d63ce2bSvenki 		return (ENOMEM);
4400d63ce2bSvenki 	}
4410d63ce2bSvenki 
4420d63ce2bSvenki 	sp = ddi_get_soft_state(ds_snmp_statep, minor);
4430d63ce2bSvenki 	if (devp != NULL)
4440d63ce2bSvenki 		major = getemajor(*devp);
4450d63ce2bSvenki 	else
4460d63ce2bSvenki 		major = ddi_driver_major(ds_snmp_devi);
4470d63ce2bSvenki 
4480d63ce2bSvenki 	sp->dev = makedevice(major, minor);
4490d63ce2bSvenki 	if (devp != NULL)
4500d63ce2bSvenki 		*devp = sp->dev;
4510d63ce2bSvenki 
4520d63ce2bSvenki 	sp->instance = minor;
4530d63ce2bSvenki 	sp->data = NULL;
4540d63ce2bSvenki 	sp->data_len = 0;
4550d63ce2bSvenki 	sp->req_id = 0;
4560d63ce2bSvenki 	sp->last_req_id = 0;
4570d63ce2bSvenki 	sp->state = DS_SNMP_READY;
4580d63ce2bSvenki 	sp->sc_reset = B_FALSE;
4590d63ce2bSvenki 
4600d63ce2bSvenki 	mutex_init(&sp->lock, NULL, MUTEX_DRIVER, NULL);
4610d63ce2bSvenki 	cv_init(&sp->state_cv, NULL, CV_DRIVER, NULL);
4620d63ce2bSvenki 
4630d63ce2bSvenki 	return (0);
4640d63ce2bSvenki }
4650d63ce2bSvenki 
4660d63ce2bSvenki static int
ds_snmp_destroy_state(dev_t dev)4670d63ce2bSvenki ds_snmp_destroy_state(dev_t dev)
4680d63ce2bSvenki {
4690d63ce2bSvenki 	ds_snmp_state_t	*sp;
4700d63ce2bSvenki 	minor_t	minor;
4710d63ce2bSvenki 
4720d63ce2bSvenki 	minor = getminor(dev);
4730d63ce2bSvenki 
4740d63ce2bSvenki 	if ((sp = ddi_get_soft_state(ds_snmp_statep, minor)) == NULL)
4750d63ce2bSvenki 		return (ENXIO);
4760d63ce2bSvenki 
4770d63ce2bSvenki 	ASSERT(sp->instance == minor);
4780d63ce2bSvenki 
4790d63ce2bSvenki 	/*
4800d63ce2bSvenki 	 * If the app has not exited cleanly, the data may not have been
4810d63ce2bSvenki 	 * read/memory freed, hence take care of that here
4820d63ce2bSvenki 	 */
4830d63ce2bSvenki 	if (sp->data) {
4840d63ce2bSvenki 		kmem_free(sp->data, sp->data_len);
4850d63ce2bSvenki 	}
4860d63ce2bSvenki 	cv_destroy(&sp->state_cv);
4870d63ce2bSvenki 	mutex_destroy(&sp->lock);
4880d63ce2bSvenki 
4890d63ce2bSvenki 	ddi_soft_state_free(ds_snmp_statep, minor);
4900d63ce2bSvenki 	ds_snmp_rel_minor(minor);
4910d63ce2bSvenki 
4920d63ce2bSvenki 	return (0);
4930d63ce2bSvenki }
4940d63ce2bSvenki 
4950d63ce2bSvenki /*ARGSUSED*/
4960d63ce2bSvenki static int
ds_snmp_open(dev_t * devp,int flag,int otyp,cred_t * credp)4970d63ce2bSvenki ds_snmp_open(dev_t *devp, int flag, int otyp, cred_t *credp)
4980d63ce2bSvenki {
4990d63ce2bSvenki 
5000d63ce2bSvenki 	if (otyp != OTYP_CHR)
5010d63ce2bSvenki 		return (EINVAL);
5020d63ce2bSvenki 
5030d63ce2bSvenki 	if (ds_snmp_instance == -1)
5040d63ce2bSvenki 		return (ENXIO);
5050d63ce2bSvenki 
5060d63ce2bSvenki 	/*
5070d63ce2bSvenki 	 * Avoid possible race condition - ds service may not be there yet
5080d63ce2bSvenki 	 */
5090d63ce2bSvenki 	mutex_enter(&ds_snmp_lock);
5100d63ce2bSvenki 	while (ds_snmp_has_service == B_FALSE) {
5110d63ce2bSvenki 		if (cv_wait_sig(&ds_snmp_service_cv, &ds_snmp_lock) == 0) {
5120d63ce2bSvenki 			mutex_exit(&ds_snmp_lock);
5130d63ce2bSvenki 			return (EINTR);
5140d63ce2bSvenki 		}
5150d63ce2bSvenki 	}
5160d63ce2bSvenki 	mutex_exit(&ds_snmp_lock);
5170d63ce2bSvenki 
5180d63ce2bSvenki 	return (ds_snmp_create_state(devp));
5190d63ce2bSvenki }
5200d63ce2bSvenki 
5210d63ce2bSvenki 
5220d63ce2bSvenki /*ARGSUSED*/
5230d63ce2bSvenki static int
ds_snmp_close(dev_t dev,int flag,int otyp,cred_t * credp)5240d63ce2bSvenki ds_snmp_close(dev_t dev, int flag, int otyp, cred_t *credp)
5250d63ce2bSvenki {
5260d63ce2bSvenki 	if (otyp != OTYP_CHR)
5270d63ce2bSvenki 		return (EINVAL);
5280d63ce2bSvenki 
5290d63ce2bSvenki 	if (ds_snmp_instance == -1)
5300d63ce2bSvenki 		return (ENXIO);
5310d63ce2bSvenki 
5320d63ce2bSvenki 	if (ds_snmp_handle == DS_INVALID_HDL)
5330d63ce2bSvenki 		return (EIO);
5340d63ce2bSvenki 
5350d63ce2bSvenki 	return (ds_snmp_destroy_state(dev));
5360d63ce2bSvenki }
5370d63ce2bSvenki 
5380d63ce2bSvenki /*ARGSUSED*/
5390d63ce2bSvenki static int
ds_snmp_read(dev_t dev,struct uio * uiop,cred_t * credp)5400d63ce2bSvenki ds_snmp_read(dev_t dev, struct uio *uiop, cred_t *credp)
5410d63ce2bSvenki {
5420d63ce2bSvenki 	ds_snmp_state_t *sp;
5430d63ce2bSvenki 	minor_t	minor;
5440d63ce2bSvenki 	size_t len;
5450d63ce2bSvenki 	int retval;
5460d63ce2bSvenki 	caddr_t tmpbufp = (caddr_t)NULL;
5470d63ce2bSvenki 
5480d63ce2bSvenki 	/*
5490d63ce2bSvenki 	 * Given that now we can have sc resets happening at any
5500d63ce2bSvenki 	 * time, it is possible that it happened since the last time
5510d63ce2bSvenki 	 * we issued a read, write or ioctl.  If so, we need to wait
5520d63ce2bSvenki 	 * for the unreg-reg pair to complete before we can do
5530d63ce2bSvenki 	 * anything.
5540d63ce2bSvenki 	 */
5550d63ce2bSvenki 	mutex_enter(&ds_snmp_lock);
5560d63ce2bSvenki 	while (ds_snmp_has_service == B_FALSE) {
5570d63ce2bSvenki 		DS_SNMP_DBG("ds_snmp_read: waiting for service\n");
5580d63ce2bSvenki 		if (cv_wait_sig(&ds_snmp_service_cv, &ds_snmp_lock) == 0) {
5590d63ce2bSvenki 			mutex_exit(&ds_snmp_lock);
5600d63ce2bSvenki 			return (EINTR);
5610d63ce2bSvenki 		}
5620d63ce2bSvenki 	}
5630d63ce2bSvenki 	mutex_exit(&ds_snmp_lock);
5640d63ce2bSvenki 
5650d63ce2bSvenki 	if ((len = uiop->uio_resid) == 0)
5660d63ce2bSvenki 		return (0);
5670d63ce2bSvenki 
5680d63ce2bSvenki 	minor = getminor(dev);
5690d63ce2bSvenki 	if ((sp = ddi_get_soft_state(ds_snmp_statep, minor)) == NULL)
5700d63ce2bSvenki 		return (ENXIO);
5710d63ce2bSvenki 
5720d63ce2bSvenki 	mutex_enter(&sp->lock);
5730d63ce2bSvenki 
5740d63ce2bSvenki 	if (sp->sc_reset == B_TRUE) {
5750d63ce2bSvenki 		mutex_exit(&sp->lock);
5760d63ce2bSvenki 		return (ECANCELED);
5770d63ce2bSvenki 	}
5780d63ce2bSvenki 
5790d63ce2bSvenki 	/*
5800d63ce2bSvenki 	 * Block or bail if there is no SNMP data
5810d63ce2bSvenki 	 */
5820d63ce2bSvenki 	if (sp->state != DS_SNMP_DATA_AVL && sp->state != DS_SNMP_DATA_ERR) {
5830d63ce2bSvenki 		DS_SNMP_DBG("ds_snmp_read: no SNMP data\n");
5840d63ce2bSvenki 		if (uiop->uio_fmode & (FNDELAY | FNONBLOCK)) {
5850d63ce2bSvenki 			mutex_exit(&sp->lock);
5860d63ce2bSvenki 			return (EAGAIN);
5870d63ce2bSvenki 		}
5880d63ce2bSvenki 		while (sp->state != DS_SNMP_DATA_AVL &&
589ece6eed9Sfw 		    sp->state != DS_SNMP_DATA_ERR) {
5900d63ce2bSvenki 			if (cv_wait_sig(&sp->state_cv, &sp->lock) == 0) {
5910d63ce2bSvenki 				mutex_exit(&sp->lock);
5920d63ce2bSvenki 				return (EINTR);
5930d63ce2bSvenki 			}
5940d63ce2bSvenki 		}
5950d63ce2bSvenki 	}
5960d63ce2bSvenki 
5970d63ce2bSvenki 	/*
5980d63ce2bSvenki 	 * If there has been an error, it could be because the agent
5990d63ce2bSvenki 	 * returned failure and there is no data to read, or an ldc-reset
6000d63ce2bSvenki 	 * has happened.  Figure out which and return appropriate
6010d63ce2bSvenki 	 * error to the caller.
6020d63ce2bSvenki 	 */
6030d63ce2bSvenki 	if (sp->state == DS_SNMP_DATA_ERR) {
6040d63ce2bSvenki 		if (sp->sc_reset == B_TRUE) {
6050d63ce2bSvenki 			mutex_exit(&sp->lock);
6060d63ce2bSvenki 			DS_SNMP_DBG("ds_snmp_read: sc got reset, "
6070d63ce2bSvenki 			    "returning ECANCELED\n");
6080d63ce2bSvenki 			return (ECANCELED);
6090d63ce2bSvenki 		} else {
6100d63ce2bSvenki 			sp->state = DS_SNMP_READY;
6110d63ce2bSvenki 			cv_broadcast(&sp->state_cv);
6120d63ce2bSvenki 			mutex_exit(&sp->lock);
6130d63ce2bSvenki 			DS_SNMP_DBG("ds_snmp_read: data error, "
6140d63ce2bSvenki 			    "returning EIO\n");
6150d63ce2bSvenki 			return (EIO);
6160d63ce2bSvenki 		}
6170d63ce2bSvenki 	}
6180d63ce2bSvenki 
6190d63ce2bSvenki 	if (len > sp->data_len)
6200d63ce2bSvenki 		len = sp->data_len;
6210d63ce2bSvenki 
6220d63ce2bSvenki 	tmpbufp = kmem_alloc(len, KM_SLEEP);
6230d63ce2bSvenki 
6240d63ce2bSvenki 	bcopy(sp->data, (void *)tmpbufp, len);
6250d63ce2bSvenki 	kmem_free(sp->data, sp->data_len);
6260d63ce2bSvenki 	sp->data = (caddr_t)NULL;
6270d63ce2bSvenki 	sp->data_len = 0;
6280d63ce2bSvenki 
6290d63ce2bSvenki 	/*
6300d63ce2bSvenki 	 * SNMP data has been consumed, wake up anyone waiting to send
6310d63ce2bSvenki 	 */
6320d63ce2bSvenki 	sp->state = DS_SNMP_READY;
6330d63ce2bSvenki 	cv_broadcast(&sp->state_cv);
6340d63ce2bSvenki 
6350d63ce2bSvenki 	mutex_exit(&sp->lock);
6360d63ce2bSvenki 
6370d63ce2bSvenki 	retval = uiomove(tmpbufp, len, UIO_READ, uiop);
6380d63ce2bSvenki 	kmem_free(tmpbufp, len);
6390d63ce2bSvenki 
6400d63ce2bSvenki 	return (retval);
6410d63ce2bSvenki }
6420d63ce2bSvenki 
6430d63ce2bSvenki /*ARGSUSED*/
6440d63ce2bSvenki static int
ds_snmp_write(dev_t dev,struct uio * uiop,cred_t * credp)6450d63ce2bSvenki ds_snmp_write(dev_t dev, struct uio *uiop, cred_t *credp)
6460d63ce2bSvenki {
6470d63ce2bSvenki 	ds_snmp_state_t *sp;
6480d63ce2bSvenki 	ds_snmp_msg_t hdr;
6490d63ce2bSvenki 	minor_t minor;
6500d63ce2bSvenki 	size_t len;
6510d63ce2bSvenki 	caddr_t tmpbufp;
652*c659a048SMichael Bergknoff 	size_t orig_size;
6530d63ce2bSvenki 
6540d63ce2bSvenki 	/*
6550d63ce2bSvenki 	 * Check if there was an sc reset; if yes, wait until we have the
6560d63ce2bSvenki 	 * service back again.
6570d63ce2bSvenki 	 */
6580d63ce2bSvenki 	mutex_enter(&ds_snmp_lock);
6590d63ce2bSvenki 	while (ds_snmp_has_service == B_FALSE) {
6600d63ce2bSvenki 		DS_SNMP_DBG("ds_snmp_write: waiting for service\n");
6610d63ce2bSvenki 		if (cv_wait_sig(&ds_snmp_service_cv, &ds_snmp_lock) == 0) {
6620d63ce2bSvenki 			mutex_exit(&ds_snmp_lock);
6630d63ce2bSvenki 			return (EINTR);
6640d63ce2bSvenki 		}
6650d63ce2bSvenki 	}
6660d63ce2bSvenki 	mutex_exit(&ds_snmp_lock);
6670d63ce2bSvenki 
6680d63ce2bSvenki 	minor = getminor(dev);
6690d63ce2bSvenki 	if ((sp = ddi_get_soft_state(ds_snmp_statep, minor)) == NULL)
6700d63ce2bSvenki 		return (ENXIO);
6710d63ce2bSvenki 
672*c659a048SMichael Bergknoff 	orig_size = uiop->uio_resid;
6730d63ce2bSvenki 	len = uiop->uio_resid + sizeof (ds_snmp_msg_t);
6740d63ce2bSvenki 	tmpbufp = kmem_alloc(len, KM_SLEEP);
6750d63ce2bSvenki 
6760d63ce2bSvenki 	if (uiomove(tmpbufp + sizeof (ds_snmp_msg_t),
6770d63ce2bSvenki 	    len - sizeof (ds_snmp_msg_t), UIO_WRITE, uiop) != 0) {
6780d63ce2bSvenki 		kmem_free(tmpbufp, len);
6790d63ce2bSvenki 		return (EIO);
6800d63ce2bSvenki 	}
6810d63ce2bSvenki 
6820d63ce2bSvenki 	mutex_enter(&sp->lock);
6830d63ce2bSvenki 
6840d63ce2bSvenki 	if (sp->sc_reset == B_TRUE) {
6850d63ce2bSvenki 		mutex_exit(&sp->lock);
6860d63ce2bSvenki 		kmem_free(tmpbufp, len);
6870d63ce2bSvenki 		DS_SNMP_DBG("ds_snmp_write: sc_reset is TRUE, "
6880d63ce2bSvenki 		    "returning ECANCELD\n");
6890d63ce2bSvenki 		return (ECANCELED);
6900d63ce2bSvenki 	}
6910d63ce2bSvenki 
6920d63ce2bSvenki 	/*
6930d63ce2bSvenki 	 * wait if earlier transaction is not yet completed
6940d63ce2bSvenki 	 */
6950d63ce2bSvenki 	while (sp->state != DS_SNMP_READY) {
6960d63ce2bSvenki 		if (cv_wait_sig(&sp->state_cv, &sp->lock) == 0) {
6970d63ce2bSvenki 			mutex_exit(&sp->lock);
6980d63ce2bSvenki 			kmem_free(tmpbufp, len);
699*c659a048SMichael Bergknoff 			uiop->uio_resid = orig_size;
7000d63ce2bSvenki 			return (EINTR);
7010d63ce2bSvenki 		}
7020d63ce2bSvenki 		/*
7030d63ce2bSvenki 		 * Normally, only a reader would ever wake us up. But if we
7040d63ce2bSvenki 		 * did get signalled with an ERROR, it could only mean there
7050d63ce2bSvenki 		 * was an sc reset and there's no point waiting; we need to
7060d63ce2bSvenki 		 * fail this write().
7070d63ce2bSvenki 		 */
7080d63ce2bSvenki 		if (sp->state == DS_SNMP_DATA_ERR && sp->sc_reset == B_TRUE) {
7090d63ce2bSvenki 			DS_SNMP_DBG("ds_snmp_write: woke up with an sc_reset, "
7100d63ce2bSvenki 			    "returning ECANCELED\n");
7110d63ce2bSvenki 			mutex_exit(&sp->lock);
7120d63ce2bSvenki 			kmem_free(tmpbufp, len);
7130d63ce2bSvenki 			return (ECANCELED);
7140d63ce2bSvenki 		}
7150d63ce2bSvenki 	}
7160d63ce2bSvenki 
7170d63ce2bSvenki 	if (sp->req_id == (((uint64_t)1 << DS_SNMP_MINOR_SHIFT) - 1))
7180d63ce2bSvenki 		sp->req_id = 0; /* Reset */
7190d63ce2bSvenki 
7200d63ce2bSvenki 	hdr.seq_num = ((uint64_t)minor << DS_SNMP_MINOR_SHIFT) | sp->req_id;
7210d63ce2bSvenki 	sp->last_req_id = hdr.seq_num;
7220d63ce2bSvenki 	(sp->req_id)++;
7230d63ce2bSvenki 
7240d63ce2bSvenki 	/*
7250d63ce2bSvenki 	 * Set state to SNMP_REQUESTED, but don't wakeup anyone yet
7260d63ce2bSvenki 	 */
7270d63ce2bSvenki 	sp->state = DS_SNMP_REQUESTED;
7280d63ce2bSvenki 
7290d63ce2bSvenki 	mutex_exit(&sp->lock);
7300d63ce2bSvenki 
7310d63ce2bSvenki 	hdr.type = DS_SNMP_REQUEST;
7320d63ce2bSvenki 	bcopy((void *)&hdr, (void *)tmpbufp, sizeof (hdr));
7330d63ce2bSvenki 
7340d63ce2bSvenki 	/*
7350d63ce2bSvenki 	 * If the service went away since the time we entered this
7360d63ce2bSvenki 	 * routine and now, tough luck. Just ignore the current
7370d63ce2bSvenki 	 * write() and return.
7380d63ce2bSvenki 	 */
7390d63ce2bSvenki 	mutex_enter(&ds_snmp_lock);
7400d63ce2bSvenki 	if (ds_snmp_has_service == B_FALSE) {
7410d63ce2bSvenki 		DS_SNMP_DBG("ds_snmp_write: service went away, aborting "
7420d63ce2bSvenki 		    "write, returning ECANCELED\n");
7430d63ce2bSvenki 		mutex_exit(&ds_snmp_lock);
7440d63ce2bSvenki 		kmem_free(tmpbufp, len);
7450d63ce2bSvenki 		return (ECANCELED);
7460d63ce2bSvenki 	}
7470d63ce2bSvenki 	DS_SNMP_DBG("ds_snmp_write: ds_cap_send(0x%lx, %lu) called.\n",
7480d63ce2bSvenki 	    ds_snmp_handle, len);
7490d63ce2bSvenki 	(void) ds_cap_send(ds_snmp_handle, tmpbufp, len);
7500d63ce2bSvenki 	mutex_exit(&ds_snmp_lock);
7510d63ce2bSvenki 
7520d63ce2bSvenki 	kmem_free(tmpbufp, len);
7530d63ce2bSvenki 
7540d63ce2bSvenki 	return (0);
7550d63ce2bSvenki }
7560d63ce2bSvenki 
7570d63ce2bSvenki /*ARGSUSED*/
7580d63ce2bSvenki static int
ds_snmp_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)7590d63ce2bSvenki ds_snmp_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
7600d63ce2bSvenki     int *rvalp)
7610d63ce2bSvenki {
7620d63ce2bSvenki 	ds_snmp_state_t *sp;
7630d63ce2bSvenki 	struct dssnmp_info info;
7640d63ce2bSvenki 	minor_t	minor;
7650d63ce2bSvenki 
7660d63ce2bSvenki 	/*
7670d63ce2bSvenki 	 * Check if there was an sc reset; if yes, wait until we have the
7680d63ce2bSvenki 	 * service back again.
7690d63ce2bSvenki 	 */
7700d63ce2bSvenki 	mutex_enter(&ds_snmp_lock);
7710d63ce2bSvenki 	while (ds_snmp_has_service == B_FALSE) {
7720d63ce2bSvenki 		DS_SNMP_DBG("ds_snmp_ioctl: waiting for service\n");
7730d63ce2bSvenki 		if (cv_wait_sig(&ds_snmp_service_cv, &ds_snmp_lock) == 0) {
7740d63ce2bSvenki 			mutex_exit(&ds_snmp_lock);
7750d63ce2bSvenki 			return (EINTR);
7760d63ce2bSvenki 		}
7770d63ce2bSvenki 	}
7780d63ce2bSvenki 	mutex_exit(&ds_snmp_lock);
7790d63ce2bSvenki 
7800d63ce2bSvenki 	DS_SNMP_DBG("ds_snmp_ioctl: hdl=0x%lx\n", ds_snmp_handle);
7810d63ce2bSvenki 
7820d63ce2bSvenki 	minor = getminor(dev);
7830d63ce2bSvenki 	if ((sp = ddi_get_soft_state(ds_snmp_statep, minor)) == NULL)
7840d63ce2bSvenki 		return (ENXIO);
7850d63ce2bSvenki 
7860d63ce2bSvenki 	if (!(mode & FREAD))
7870d63ce2bSvenki 		return (EACCES);
7880d63ce2bSvenki 
7890d63ce2bSvenki 	switch (cmd) {
7900d63ce2bSvenki 	case DSSNMP_GETINFO:
7910d63ce2bSvenki 		mutex_enter(&sp->lock);
7920d63ce2bSvenki 
7930d63ce2bSvenki 		if (sp->sc_reset == B_TRUE) {
7940d63ce2bSvenki 			mutex_exit(&sp->lock);
7950d63ce2bSvenki 			DS_SNMP_DBG("ds_snmp_ioctl: returning ECANCELED\n");
7960d63ce2bSvenki 			return (ECANCELED);
7970d63ce2bSvenki 		}
7980d63ce2bSvenki 
7990d63ce2bSvenki 		while (sp->state != DS_SNMP_DATA_AVL &&
8000d63ce2bSvenki 		    sp->state != DS_SNMP_DATA_ERR) {
8010d63ce2bSvenki 			DS_SNMP_DBG("ds_snmp_ioctl: state=%d, sc_reset=%d, "
8020d63ce2bSvenki 			    "waiting for data\n", sp->state, sp->sc_reset);
8030d63ce2bSvenki 			if (cv_wait_sig(&sp->state_cv, &sp->lock) == 0) {
804ece6eed9Sfw 				sp->state = DS_SNMP_READY;
8050d63ce2bSvenki 				mutex_exit(&sp->lock);
8060d63ce2bSvenki 				return (EINTR);
8070d63ce2bSvenki 			}
8080d63ce2bSvenki 		}
8090d63ce2bSvenki 		DS_SNMP_DBG("ds_snmp_ioctl: state=%d, sc_reset=%d, "
8100d63ce2bSvenki 		    "out of wait!\n", sp->state, sp->sc_reset);
8110d63ce2bSvenki 
8120d63ce2bSvenki 		/*
8130d63ce2bSvenki 		 * If there has been an error, it could be because the
8140d63ce2bSvenki 		 * agent returned failure and there is no data to read,
8150d63ce2bSvenki 		 * or an ldc-reset has happened.  Figure out which and
8160d63ce2bSvenki 		 * return appropriate error to the caller.
8170d63ce2bSvenki 		 */
8180d63ce2bSvenki 		if (sp->state == DS_SNMP_DATA_ERR) {
8190d63ce2bSvenki 			if (sp->sc_reset == B_TRUE) {
8200d63ce2bSvenki 				mutex_exit(&sp->lock);
8210d63ce2bSvenki 				DS_SNMP_DBG("ds_snmp_ioctl: sc_reset=TRUE "
8220d63ce2bSvenki 				    "returning ECANCELED\n");
8230d63ce2bSvenki 				return (ECANCELED);
8240d63ce2bSvenki 			} else {
8250d63ce2bSvenki 				sp->state = DS_SNMP_READY;
8260d63ce2bSvenki 				cv_broadcast(&sp->state_cv);
8270d63ce2bSvenki 				mutex_exit(&sp->lock);
8280d63ce2bSvenki 				DS_SNMP_DBG("ds_snmp_ioctl: sc_reset=FALSE "
8290d63ce2bSvenki 				    "returning EIO\n");
8300d63ce2bSvenki 				return (EIO);
8310d63ce2bSvenki 			}
8320d63ce2bSvenki 		}
8330d63ce2bSvenki 
8340d63ce2bSvenki 		info.size = sp->data_len;
8350d63ce2bSvenki 		info.token = sp->gencount;
8360d63ce2bSvenki 
8370d63ce2bSvenki 		mutex_exit(&sp->lock);
8380d63ce2bSvenki 
8390d63ce2bSvenki 		if (ddi_copyout(&info, (void *)arg, sizeof (info), mode) != 0)
8400d63ce2bSvenki 			return (EFAULT);
8410d63ce2bSvenki 		break;
8420d63ce2bSvenki 
8430d63ce2bSvenki 	case DSSNMP_CLRLNKRESET:
8440d63ce2bSvenki 		mutex_enter(&sp->lock);
8450d63ce2bSvenki 
8460d63ce2bSvenki 		DS_SNMP_DBG("ds_snmp_ioctl: DSSNMP_CLRLNKRESET\n");
8470d63ce2bSvenki 		DS_SNMP_DBG("ds_snmp_ioctl: sc_reset=%d\n", sp->sc_reset);
8480d63ce2bSvenki 
8490d63ce2bSvenki 		if (sp->sc_reset == B_TRUE) {
8500d63ce2bSvenki 			if (sp->data) {
8510d63ce2bSvenki 				DS_SNMP_DBG("ds_snmp_ioctl: data=%p, len=%lu\n",
8520d63ce2bSvenki 				    sp->data, sp->data_len);
8530d63ce2bSvenki 				kmem_free(sp->data, sp->data_len);
8540d63ce2bSvenki 			}
8550d63ce2bSvenki 			sp->data = NULL;
8560d63ce2bSvenki 			sp->data_len = 0;
8570d63ce2bSvenki 			sp->state = DS_SNMP_READY;
8580d63ce2bSvenki 			sp->req_id = 0;
8590d63ce2bSvenki 			sp->last_req_id = 0;
8600d63ce2bSvenki 			sp->sc_reset = B_FALSE;
8610d63ce2bSvenki 		}
8620d63ce2bSvenki 		mutex_exit(&sp->lock);
8630d63ce2bSvenki 		break;
8640d63ce2bSvenki 
8650d63ce2bSvenki 	default:
8660d63ce2bSvenki 		return (ENOTTY);
8670d63ce2bSvenki 	}
8680d63ce2bSvenki 
8690d63ce2bSvenki 	return (0);
8700d63ce2bSvenki }
8710d63ce2bSvenki 
8720d63ce2bSvenki /*
8730d63ce2bSvenki  * DS Callbacks
8740d63ce2bSvenki  */
8750d63ce2bSvenki /*ARGSUSED*/
8760d63ce2bSvenki static void
ds_snmp_reg_handler(ds_cb_arg_t arg,ds_ver_t * ver,ds_svc_hdl_t hdl)8770d63ce2bSvenki ds_snmp_reg_handler(ds_cb_arg_t arg, ds_ver_t *ver, ds_svc_hdl_t hdl)
8780d63ce2bSvenki {
8790d63ce2bSvenki 	DS_SNMP_DBG("ds_snmp_reg_handler: registering handle 0x%lx for version "
8800d63ce2bSvenki 	    "0x%x:0x%x\n", (uint64_t)hdl, ver->major, ver->minor);
8810d63ce2bSvenki 
8820d63ce2bSvenki 	mutex_enter(&ds_snmp_lock);
8830d63ce2bSvenki 
8840d63ce2bSvenki 	ASSERT(ds_snmp_handle == DS_INVALID_HDL);
8850d63ce2bSvenki 
8860d63ce2bSvenki 	ds_snmp_handle = hdl;
8870d63ce2bSvenki 	ds_snmp_has_service = B_TRUE;
8880d63ce2bSvenki 
8890d63ce2bSvenki 	cv_broadcast(&ds_snmp_service_cv);
8900d63ce2bSvenki 
8910d63ce2bSvenki 	mutex_exit(&ds_snmp_lock);
8920d63ce2bSvenki 
8930d63ce2bSvenki }
8940d63ce2bSvenki 
8950d63ce2bSvenki /*ARGSUSED*/
8960d63ce2bSvenki static void
ds_snmp_unreg_handler(ds_cb_arg_t arg)8970d63ce2bSvenki ds_snmp_unreg_handler(ds_cb_arg_t arg)
8980d63ce2bSvenki {
8990d63ce2bSvenki 	minor_t minor;
9000d63ce2bSvenki 	ds_snmp_state_t *sp;
9010d63ce2bSvenki 
9020d63ce2bSvenki 	DS_SNMP_DBG("ds_snmp_unreg_handler: un-registering ds_snmp service\n");
9030d63ce2bSvenki 
9040d63ce2bSvenki 	mutex_enter(&ds_snmp_lock);
9050d63ce2bSvenki 
9060d63ce2bSvenki 	if (ds_snmp_num_opens) {
9070d63ce2bSvenki 		DS_SNMP_DBG("ds_snmp_unreg_handler: %d opens, sc reset!\n",
9080d63ce2bSvenki 		    ds_snmp_num_opens);
9090d63ce2bSvenki 		for (minor = 1; minor <= DS_SNMP_MAX_OPENS; minor++) {
9100d63ce2bSvenki 			if (ds_snmp_is_open(minor)) {
9110d63ce2bSvenki 				DS_SNMP_DBG("ds_snmp_unreg_handler: minor %d "
9120d63ce2bSvenki 				    "open\n", minor);
9130d63ce2bSvenki 				sp = ddi_get_soft_state(ds_snmp_statep, minor);
9140d63ce2bSvenki 				if (sp == NULL)
9150d63ce2bSvenki 					continue;
9160d63ce2bSvenki 
9170d63ce2bSvenki 				/*
9180d63ce2bSvenki 				 * Set the sc_reset flag and break any waiters
9190d63ce2bSvenki 				 * out of their existing reads/writes/ioctls.
9200d63ce2bSvenki 				 */
9210d63ce2bSvenki 				DS_SNMP_DBG("ds_snmp_unreg_hdlr: about to "
9220d63ce2bSvenki 				    "signal waiters\n");
9230d63ce2bSvenki 				mutex_enter(&sp->lock);
9240d63ce2bSvenki 				sp->sc_reset = B_TRUE;
9250d63ce2bSvenki 				sp->state = DS_SNMP_DATA_ERR;
9260d63ce2bSvenki 				cv_broadcast(&sp->state_cv);
9270d63ce2bSvenki 				mutex_exit(&sp->lock);
9280d63ce2bSvenki 			}
9290d63ce2bSvenki 		}
9300d63ce2bSvenki 	}
9310d63ce2bSvenki 
9320d63ce2bSvenki 	ds_snmp_handle = DS_INVALID_HDL;
9330d63ce2bSvenki 	ds_snmp_has_service = B_FALSE;
9340d63ce2bSvenki 
9350d63ce2bSvenki 	DS_SNMP_DBG("ds_snmp_unreg_handler: handle invalidated\n");
9360d63ce2bSvenki 
9370d63ce2bSvenki 	mutex_exit(&ds_snmp_lock);
9380d63ce2bSvenki }
9390d63ce2bSvenki 
9400d63ce2bSvenki /*ARGSUSED*/
9410d63ce2bSvenki static void
ds_snmp_data_handler(ds_cb_arg_t arg,void * buf,size_t buflen)9420d63ce2bSvenki ds_snmp_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen)
9430d63ce2bSvenki {
9440d63ce2bSvenki 	ds_snmp_state_t *sp;
9450d63ce2bSvenki 	ds_snmp_msg_t   hdr;
9460d63ce2bSvenki 	size_t  	snmp_size;
9470d63ce2bSvenki 	minor_t 	minor;
9480d63ce2bSvenki 
9490d63ce2bSvenki 	/*
9500d63ce2bSvenki 	 * Make sure the header is at least valid
9510d63ce2bSvenki 	 */
9520d63ce2bSvenki 	if (buflen < sizeof (hdr)) {
9530d63ce2bSvenki 		cmn_err(CE_WARN,
9540d63ce2bSvenki 		"ds_snmp_data_handler: buflen <%lu> too small", buflen);
9550d63ce2bSvenki 		return;
9560d63ce2bSvenki 	}
9570d63ce2bSvenki 
9580d63ce2bSvenki 	ASSERT(buf != NULL);
9590d63ce2bSvenki 	bcopy(buf, (void *)&hdr, sizeof (hdr));
9600d63ce2bSvenki 
9610d63ce2bSvenki 	DS_SNMP_DBG("ds_snmp_data_handler: msg buf len 0x%lx : type 0x%lx, "
9620d63ce2bSvenki 	    "seqn 0x%lx\n", buflen, hdr.type, hdr.seq_num);
9630d63ce2bSvenki 
9640d63ce2bSvenki 	minor = (int)(hdr.seq_num >> DS_SNMP_MINOR_SHIFT);
9650d63ce2bSvenki 	if ((sp = ddi_get_soft_state(ds_snmp_statep, minor)) == NULL)
9660d63ce2bSvenki 		return;
9670d63ce2bSvenki 
9680d63ce2bSvenki 	mutex_enter(&sp->lock);
9690d63ce2bSvenki 
9700d63ce2bSvenki 	/*
9710d63ce2bSvenki 	 * If there is no pending SNMP request, then we've received
972ece6eed9Sfw 	 * bogus data or an SNMP trap or the reader was interrupted.
973ece6eed9Sfw 	 * Since we don't yet support SNMP traps, ignore it.
9740d63ce2bSvenki 	 */
9750d63ce2bSvenki 	if (sp->state != DS_SNMP_REQUESTED) {
976ece6eed9Sfw 		DS_SNMP_DBG("Received SNMP data without request");
9770d63ce2bSvenki 		mutex_exit(&sp->lock);
9780d63ce2bSvenki 		return;
9790d63ce2bSvenki 	}
9800d63ce2bSvenki 
9810d63ce2bSvenki 	/*
9820d63ce2bSvenki 	 * Response to a request therefore old SNMP must've been consumed
9830d63ce2bSvenki 	 */
9840d63ce2bSvenki 	ASSERT(sp->data_len == 0);
9850d63ce2bSvenki 	ASSERT(sp->data == NULL);
9860d63ce2bSvenki 
9870d63ce2bSvenki 	/*
9880d63ce2bSvenki 	 * Response seq_num should match our request seq_num
9890d63ce2bSvenki 	 */
9900d63ce2bSvenki 	if (hdr.seq_num != sp->last_req_id) {
9910d63ce2bSvenki 		cmn_err(CE_WARN, "Received DS snmp data out of sequence with "
9920d63ce2bSvenki 		    "request");
9930d63ce2bSvenki 		mutex_exit(&sp->lock);
9940d63ce2bSvenki 		return;
9950d63ce2bSvenki 	}
9960d63ce2bSvenki 
9970d63ce2bSvenki 	if (hdr.type == DS_SNMP_ERROR) {
9980d63ce2bSvenki 		sp->state = DS_SNMP_DATA_ERR;
9990d63ce2bSvenki 		DS_SNMP_DBG("ds_snmp_data_handler: hdr.type = DS_SNMP_ERROR\n");
10000d63ce2bSvenki 	} else {
10010d63ce2bSvenki 		snmp_size = buflen - sizeof (ds_snmp_msg_t);
10020d63ce2bSvenki 		sp->data = kmem_alloc(snmp_size, KM_SLEEP);
10030d63ce2bSvenki 		sp->data_len = snmp_size;
10040d63ce2bSvenki 		sp->state = DS_SNMP_DATA_AVL;
10050d63ce2bSvenki 
10060d63ce2bSvenki 		bcopy((caddr_t)buf + sizeof (ds_snmp_msg_t),
10070d63ce2bSvenki 		    sp->data, sp->data_len);
10080d63ce2bSvenki 	}
10090d63ce2bSvenki 
10100d63ce2bSvenki 	sp->gencount++;
10110d63ce2bSvenki 
10120d63ce2bSvenki 	/*
10130d63ce2bSvenki 	 * Wake up any readers waiting for data
10140d63ce2bSvenki 	 */
10150d63ce2bSvenki 	cv_broadcast(&sp->state_cv);
10160d63ce2bSvenki 	mutex_exit(&sp->lock);
10170d63ce2bSvenki }
1018