Home | History | Annotate | Line # | Download | only in sysmon
sysmon.c revision 1.20
      1 /*	$NetBSD: sysmon.c,v 1.20 2015/04/23 23:22:03 pgoyette Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2000 Zembu Labs, Inc.
      5  * All rights reserved.
      6  *
      7  * Author: Jason R. Thorpe <thorpej (at) zembu.com>
      8  *
      9  * Redistribution and use in source and binary forms, with or without
     10  * modification, are permitted provided that the following conditions
     11  * are met:
     12  * 1. Redistributions of source code must retain the above copyright
     13  *    notice, this list of conditions and the following disclaimer.
     14  * 2. Redistributions in binary form must reproduce the above copyright
     15  *    notice, this list of conditions and the following disclaimer in the
     16  *    documentation and/or other materials provided with the distribution.
     17  * 3. All advertising materials mentioning features or use of this software
     18  *    must display the following acknowledgement:
     19  *	This product includes software developed by Zembu Labs, Inc.
     20  * 4. Neither the name of Zembu Labs nor the names of its employees may
     21  *    be used to endorse or promote products derived from this software
     22  *    without specific prior written permission.
     23  *
     24  * THIS SOFTWARE IS PROVIDED BY ZEMBU LABS, INC. ``AS IS'' AND ANY EXPRESS
     25  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WAR-
     26  * RANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DIS-
     27  * CLAIMED.  IN NO EVENT SHALL ZEMBU LABS BE LIABLE FOR ANY DIRECT, INDIRECT,
     28  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     29  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     30  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     31  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     32  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     33  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     34  */
     35 
     36 /*
     37  * Clearing house for system monitoring hardware.  We currently
     38  * handle environmental sensors, watchdog timers, and power management.
     39  */
     40 
     41 #include <sys/cdefs.h>
     42 __KERNEL_RCSID(0, "$NetBSD: sysmon.c,v 1.20 2015/04/23 23:22:03 pgoyette Exp $");
     43 
     44 #include <sys/param.h>
     45 #include <sys/conf.h>
     46 #include <sys/errno.h>
     47 #include <sys/fcntl.h>
     48 #include <sys/callout.h>
     49 #include <sys/kernel.h>
     50 #include <sys/systm.h>
     51 #include <sys/proc.h>
     52 #include <sys/module.h>
     53 #include <sys/mutex.h>
     54 #include <sys/device.h>
     55 
     56 #include <dev/sysmon/sysmonvar.h>
     57 
     58 dev_type_open(sysmonopen);
     59 dev_type_close(sysmonclose);
     60 dev_type_ioctl(sysmonioctl);
     61 dev_type_read(sysmonread);
     62 dev_type_poll(sysmonpoll);
     63 dev_type_kqfilter(sysmonkqfilter);
     64 
     65 const struct cdevsw sysmon_cdevsw = {
     66 	.d_open = sysmonopen,
     67 	.d_close = sysmonclose,
     68 	.d_read = sysmonread,
     69 	.d_write = nowrite,
     70 	.d_ioctl = sysmonioctl,
     71 	.d_stop = nostop,
     72 	.d_tty = notty,
     73 	.d_poll = sysmonpoll,
     74 	.d_mmap = nommap,
     75 	.d_kqfilter = sysmonkqfilter,
     76 	.d_discard = nodiscard,
     77 	.d_flag = D_OTHER | D_MPSAFE
     78 };
     79 
     80 static int	sysmon_match(device_t, cfdata_t, void *);
     81 static void	sysmon_attach(device_t, device_t, void *);
     82 static int	sysmon_detach(device_t, int);
     83 
     84 static int	sysmon_modcmd(modcmd_t, void *);
     85 
     86 CFDRIVER_DECL(sysmon, DV_DULL, NULL);
     87 
     88 /*
     89  * Info about our minor "devices"
     90  */
     91 static struct sysmon_opvec	*sysmon_opvec_table[] = { NULL, NULL, NULL };
     92 static int			sysmon_refcnt[] = { 0, 0, 0 };
     93 static const char		*sysmon_mod[] = { "sysmon_envsys",
     94 						  "sysmon_wdog",
     95 						  "sysmon_power" };
     96 
     97 struct sysmon_softc {
     98 	device_t sc_dev;
     99 	kmutex_t sc_minor_mtx;
    100 };
    101 
    102 static device_t sysmon_dev = NULL;
    103 
    104 CFATTACH_DECL_NEW(sysmon, sizeof(struct sysmon_softc),
    105         sysmon_match, sysmon_attach, sysmon_detach, NULL);
    106 extern struct cfdriver sysmon_cd;
    107 
    108 static int
    109 sysmon_match(device_t parent, cfdata_t data, void *aux)
    110 {
    111 
    112 	return 1;
    113 }
    114 
    115 static void
    116 sysmon_attach(device_t parent, device_t self, void *aux)
    117 {
    118 
    119         struct sysmon_softc *sc = device_private(self);
    120 
    121         sc->sc_dev = self;
    122 
    123 	mutex_init(&sc->sc_minor_mtx, MUTEX_DEFAULT, IPL_NONE);
    124 }
    125 
    126 static int
    127 sysmon_detach(device_t self, int flags)
    128 {
    129         struct sysmon_softc *sc = device_private(self);
    130 
    131 	mutex_destroy(&sc->sc_minor_mtx);
    132 	return 0;
    133 }
    134 
    135 /*
    136  * sysmon_attach_minor
    137  *
    138  *	Attach a minor device for wdog, power, or envsys.  Manage a
    139  *	reference count so we can prevent the device from being
    140  *	detached if there are still users with the minor device opened.
    141  *
    142  *	If the opvec argument is NULL, this is a request to detach the
    143  *	minor device - make sure the refcnt is zero!
    144  */
    145 int
    146 sysmon_attach_minor(int minor, struct sysmon_opvec *opvec)
    147 {
    148 	struct sysmon_softc *sc = device_private(sysmon_dev);
    149 	int ret;
    150 
    151 	mutex_enter(&sc->sc_minor_mtx);
    152 	if (opvec) {
    153 		if (sysmon_opvec_table[minor] == NULL) {
    154 			sysmon_refcnt[minor] = 0;
    155 			sysmon_opvec_table[minor] = opvec;
    156 			ret = 0;
    157 		} else
    158 			ret = EEXIST;
    159 	} else {
    160 		if (sysmon_refcnt[minor] == 0) {
    161 			sysmon_opvec_table[minor] = NULL;
    162 			ret = 0;
    163 		} else
    164 			ret = EBUSY;
    165 	}
    166 
    167 	mutex_exit(&sc->sc_minor_mtx);
    168 	return ret;
    169 }
    170 
    171 /*
    172  * sysmonopen:
    173  *
    174  *	Open the system monitor device.
    175  */
    176 int
    177 sysmonopen(dev_t dev, int flag, int mode, struct lwp *l)
    178 {
    179 	struct sysmon_softc *sc = device_private(sysmon_dev);
    180 	int error;
    181 
    182 	mutex_enter(&sc->sc_minor_mtx);
    183 
    184 	switch (minor(dev)) {
    185 	case SYSMON_MINOR_ENVSYS:
    186 	case SYSMON_MINOR_WDOG:
    187 	case SYSMON_MINOR_POWER:
    188 		if (sysmon_opvec_table[minor(dev)] == NULL) {
    189 			mutex_exit(&sc->sc_minor_mtx);
    190 			error = module_autoload(sysmon_mod[minor(dev)],
    191 						MODULE_CLASS_MISC);
    192 			mutex_enter(&sc->sc_minor_mtx);
    193 			if (sysmon_opvec_table[minor(dev)] == NULL)
    194 				error = ENODEV;
    195 		}
    196 		error = (sysmon_opvec_table[minor(dev)]->so_open)(dev, flag,
    197 		    mode, l);
    198 		if (error == 0)
    199 			sysmon_refcnt[minor(dev)]++;
    200 		break;
    201 	default:
    202 		error = ENODEV;
    203 	}
    204 
    205 	mutex_exit(&sc->sc_minor_mtx);
    206 	return (error);
    207 }
    208 
    209 /*
    210  * sysmonclose:
    211  *
    212  *	Close the system monitor device.
    213  */
    214 int
    215 sysmonclose(dev_t dev, int flag, int mode, struct lwp *l)
    216 {
    217 	int error;
    218 
    219 	switch (minor(dev)) {
    220 	case SYSMON_MINOR_ENVSYS:
    221 	case SYSMON_MINOR_WDOG:
    222 	case SYSMON_MINOR_POWER:
    223 		if (sysmon_opvec_table[minor(dev)] == NULL)
    224 			error = ENODEV;
    225 		else {
    226 			error = (sysmon_opvec_table[minor(dev)]->so_close)(dev,
    227 			    flag, mode, l);
    228 			if (error == 0) {
    229 				sysmon_refcnt[minor(dev)]--;
    230 				KASSERT(sysmon_refcnt[minor(dev)] >= 0);
    231 			}
    232 		}
    233 		break;
    234 	default:
    235 		error = ENODEV;
    236 	}
    237 
    238 	return (error);
    239 }
    240 
    241 /*
    242  * sysmonioctl:
    243  *
    244  *	Perform a control request.
    245  */
    246 int
    247 sysmonioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
    248 {
    249 	int error;
    250 
    251 	switch (minor(dev)) {
    252 	case SYSMON_MINOR_ENVSYS:
    253 	case SYSMON_MINOR_WDOG:
    254 	case SYSMON_MINOR_POWER:
    255 		if (sysmon_opvec_table[minor(dev)] == NULL)
    256 			error = ENODEV;
    257 		else
    258 			error = (sysmon_opvec_table[minor(dev)]->so_ioctl)(dev,
    259 			    cmd, data, flag, l);
    260 		break;
    261 	default:
    262 		error = ENODEV;
    263 	}
    264 
    265 	return (error);
    266 }
    267 
    268 /*
    269  * sysmonread:
    270  *
    271  *	Perform a read request.
    272  */
    273 int
    274 sysmonread(dev_t dev, struct uio *uio, int flags)
    275 {
    276 	int error;
    277 
    278 	switch (minor(dev)) {
    279 	case SYSMON_MINOR_POWER:
    280 		if (sysmon_opvec_table[minor(dev)] == NULL)
    281 			error = ENODEV;
    282 		else
    283 			error = (sysmon_opvec_table[minor(dev)]->so_read)(dev,
    284 			    uio, flags);
    285 		break;
    286 	default:
    287 		error = ENODEV;
    288 	}
    289 
    290 	return (error);
    291 }
    292 
    293 /*
    294  * sysmonpoll:
    295  *
    296  *	Poll the system monitor device.
    297  */
    298 int
    299 sysmonpoll(dev_t dev, int events, struct lwp *l)
    300 {
    301 	int rv;
    302 
    303 	switch (minor(dev)) {
    304 	case SYSMON_MINOR_POWER:
    305 		if (sysmon_opvec_table[minor(dev)] == NULL)
    306 			rv = events;
    307 		else
    308 			rv = (sysmon_opvec_table[minor(dev)]->so_poll)(dev,
    309 			    events, l);
    310 		break;
    311 	default:
    312 		rv = events;
    313 	}
    314 
    315 	return (rv);
    316 }
    317 
    318 /*
    319  * sysmonkqfilter:
    320  *
    321  *	Kqueue filter for the system monitor device.
    322  */
    323 int
    324 sysmonkqfilter(dev_t dev, struct knote *kn)
    325 {
    326 	int error;
    327 
    328 	switch (minor(dev)) {
    329 	case SYSMON_MINOR_POWER:
    330 		if (sysmon_opvec_table[minor(dev)] == NULL)
    331 			error = ENODEV;
    332 		else
    333 			error = (sysmon_opvec_table[minor(dev)]->so_filter)(dev,
    334 			    kn);
    335 		break;
    336 	default:
    337 		error = 1;
    338 	}
    339 
    340 	return (error);
    341 }
    342 
    343 MODULE(MODULE_CLASS_DRIVER, sysmon, "");
    344 
    345 int
    346 sysmon_init(void)
    347 {
    348 	devmajor_t bmajor, cmajor;
    349 	static struct cfdata cf;
    350 	int error = 0;
    351 
    352 	if (sysmon_dev != NULL) {
    353 		return EEXIST;
    354 	}
    355 
    356 	error = config_cfdriver_attach(&sysmon_cd);
    357 	if (error) {
    358 		aprint_error("%s: unable to attach cfdriver\n",
    359 		    sysmon_cd.cd_name);
    360 		return error;
    361 	}
    362 	error = config_cfattach_attach(sysmon_cd.cd_name, &sysmon_ca);
    363 	if (error) {
    364 		config_cfdriver_detach(&sysmon_cd);
    365 		aprint_error("%s: unable to attach cfattach\n",
    366 		    sysmon_cd.cd_name);
    367 		return error;
    368 	}
    369 
    370 	bmajor = cmajor = -1;
    371 	error = devsw_attach("sysmon", NULL, &bmajor,
    372 			&sysmon_cdevsw, &cmajor);
    373 	if (error) {
    374 		config_cfattach_detach(sysmon_cd.cd_name, &sysmon_ca);
    375 		config_cfdriver_detach(&sysmon_cd);
    376 		aprint_error("%s: unable to attach devsw\n",
    377 		    sysmon_cd.cd_name);
    378 		return error;
    379 	}
    380 
    381 	cf.cf_name = sysmon_cd.cd_name;
    382 	cf.cf_atname = sysmon_cd.cd_name;
    383 	cf.cf_unit = 0;
    384 	cf.cf_fstate = FSTATE_STAR;
    385 	cf.cf_pspec = NULL;
    386 	cf.cf_loc = NULL;
    387 	cf.cf_flags = 0;
    388 
    389 	sysmon_dev = config_attach_pseudo(&cf);
    390 	if (sysmon_dev == NULL) {
    391 		aprint_error("%s: failed to attach pseudo device\n",
    392 		    sysmon_cd.cd_name);
    393 		error = ENODEV;
    394 	}
    395 
    396 	return error;
    397 }
    398 
    399 int
    400 sysmon_fini(void)
    401 {
    402 	int error = 0;
    403 
    404 	if (sysmon_opvec_table[SYSMON_MINOR_ENVSYS] != NULL)
    405 		error = EBUSY;
    406 	else if (sysmon_opvec_table[SYSMON_MINOR_WDOG] != NULL)
    407 		error = EBUSY;
    408 	else if (sysmon_opvec_table[SYSMON_MINOR_POWER] != NULL)
    409 		error = EBUSY;
    410 
    411 	else {
    412 		config_detach(sysmon_dev, 0);
    413 		devsw_detach(NULL, &sysmon_cdevsw);
    414 		config_cfattach_detach(sysmon_cd.cd_name, &sysmon_ca);
    415 		config_cfdriver_detach(&sysmon_cd);
    416 	}
    417 	if (error == 0)
    418 		sysmon_dev = NULL;
    419 
    420 	return error;
    421 }
    422 
    423 static
    424 int
    425 sysmon_modcmd(modcmd_t cmd, void *arg)
    426 {
    427 	int ret;
    428 
    429 	switch (cmd) {
    430 	case MODULE_CMD_INIT:
    431 		ret = sysmon_init();
    432 		break;
    433 
    434 	case MODULE_CMD_FINI:
    435 		ret = sysmon_fini();
    436 		break;
    437 
    438 	case MODULE_CMD_STAT:
    439 	default:
    440 		ret = ENOTTY;
    441 	}
    442 
    443 	return ret;
    444 }
    445