1/* $NetBSD: sysmon.c,v 1.32 2022/03/28 12:33:21 riastradh Exp $ */ 2 3/*- 4 * Copyright (c) 2000 Zembu Labs, Inc. 5 * All rights reserved. 6 * 7 * Author: Jason R. Thorpe <thorpej@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.32 2022/03/28 12:33:21 riastradh 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#include <sys/once.h> 56 57#include <dev/sysmon/sysmonvar.h> 58 59dev_type_open(sysmonopen); 60dev_type_close(sysmonclose); 61dev_type_ioctl(sysmonioctl); 62dev_type_read(sysmonread); 63dev_type_poll(sysmonpoll); 64dev_type_kqfilter(sysmonkqfilter); 65 66const struct cdevsw sysmon_cdevsw = { 67 .d_open = sysmonopen, 68 .d_close = sysmonclose, 69 .d_read = sysmonread, 70 .d_write = nowrite, 71 .d_ioctl = sysmonioctl, 72 .d_stop = nostop, 73 .d_tty = notty, 74 .d_poll = sysmonpoll, 75 .d_mmap = nommap, 76 .d_kqfilter = sysmonkqfilter, 77 .d_discard = nodiscard, 78 .d_flag = D_OTHER | D_MPSAFE 79}; 80 81static int sysmon_modcmd(modcmd_t, void *); 82static int sm_init_once(void); 83 84/* 85 * Info about our minor "devices" 86 */ 87static struct sysmon_opvec *sysmon_opvec_table[] = { NULL, NULL, NULL }; 88static int sysmon_refcnt[] = { 0, 0, 0 }; 89static const char *sysmon_mod[] = { "sysmon_envsys", 90 "sysmon_wdog", 91 "sysmon_power" }; 92static kmutex_t sysmon_minor_mtx; 93 94#ifdef _MODULE 95static bool sm_is_attached; 96#endif 97 98ONCE_DECL(once_sm); 99 100/* 101 * sysmon_attach_minor 102 * 103 * Attach a minor device for wdog, power, or envsys. Manage a 104 * reference count so we can prevent the device from being 105 * detached if there are still users with the minor device opened. 106 * 107 * If the opvec argument is NULL, this is a request to detach the 108 * minor device - make sure the refcnt is zero! 109 */ 110int 111sysmon_attach_minor(int minor, struct sysmon_opvec *opvec) 112{ 113 int ret; 114 115 mutex_enter(&sysmon_minor_mtx); 116 if (opvec) { 117 if (sysmon_opvec_table[minor] == NULL) { 118 sysmon_refcnt[minor] = 0; 119 sysmon_opvec_table[minor] = opvec; 120 ret = 0; 121 } else 122 ret = EEXIST; 123 } else { 124 if (sysmon_refcnt[minor] == 0) { 125 sysmon_opvec_table[minor] = NULL; 126 ret = 0; 127 } else 128 ret = EBUSY; 129 } 130 131 mutex_exit(&sysmon_minor_mtx); 132 return ret; 133} 134 135/* 136 * sysmonopen: 137 * 138 * Open the system monitor device. 139 */ 140int 141sysmonopen(dev_t dev, int flag, int mode, struct lwp *l) 142{ 143 int error; 144 145 mutex_enter(&sysmon_minor_mtx); 146 147 switch (minor(dev)) { 148 case SYSMON_MINOR_ENVSYS: 149 case SYSMON_MINOR_WDOG: 150 case SYSMON_MINOR_POWER: 151 if (sysmon_opvec_table[minor(dev)] == NULL) { 152 mutex_exit(&sysmon_minor_mtx); 153 error = module_autoload(sysmon_mod[minor(dev)], 154 MODULE_CLASS_DRIVER); 155 if (error) 156 return error; 157 mutex_enter(&sysmon_minor_mtx); 158 if (sysmon_opvec_table[minor(dev)] == NULL) { 159 error = ENODEV; 160 break; 161 } 162 } 163 error = (sysmon_opvec_table[minor(dev)]->so_open)(dev, flag, 164 mode, l); 165 if (error == 0) 166 sysmon_refcnt[minor(dev)]++; 167 break; 168 default: 169 error = ENODEV; 170 } 171 172 mutex_exit(&sysmon_minor_mtx); 173 return error; 174} 175 176/* 177 * sysmonclose: 178 * 179 * Close the system monitor device. 180 */ 181int 182sysmonclose(dev_t dev, int flag, int mode, struct lwp *l) 183{ 184 int error; 185 186 switch (minor(dev)) { 187 case SYSMON_MINOR_ENVSYS: 188 case SYSMON_MINOR_WDOG: 189 case SYSMON_MINOR_POWER: 190 if (sysmon_opvec_table[minor(dev)] == NULL) 191 error = ENODEV; 192 else { 193 error = (sysmon_opvec_table[minor(dev)]->so_close)(dev, 194 flag, mode, l); 195 if (error == 0) { 196 sysmon_refcnt[minor(dev)]--; 197 KASSERT(sysmon_refcnt[minor(dev)] >= 0); 198 } 199 } 200 break; 201 default: 202 error = ENODEV; 203 } 204 205 return (error); 206} 207 208/* 209 * sysmonioctl: 210 * 211 * Perform a control request. 212 */ 213int 214sysmonioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) 215{ 216 int error; 217 218 switch (minor(dev)) { 219 case SYSMON_MINOR_ENVSYS: 220 case SYSMON_MINOR_WDOG: 221 case SYSMON_MINOR_POWER: 222 if (sysmon_opvec_table[minor(dev)] == NULL) 223 error = ENODEV; 224 else 225 error = (sysmon_opvec_table[minor(dev)]->so_ioctl)(dev, 226 cmd, data, flag, l); 227 break; 228 default: 229 error = ENODEV; 230 } 231 232 return (error); 233} 234 235/* 236 * sysmonread: 237 * 238 * Perform a read request. 239 */ 240int 241sysmonread(dev_t dev, struct uio *uio, int flags) 242{ 243 int error; 244 245 switch (minor(dev)) { 246 case SYSMON_MINOR_POWER: 247 if (sysmon_opvec_table[minor(dev)] == NULL) 248 error = ENODEV; 249 else 250 error = (sysmon_opvec_table[minor(dev)]->so_read)(dev, 251 uio, flags); 252 break; 253 default: 254 error = ENODEV; 255 } 256 257 return (error); 258} 259 260/* 261 * sysmonpoll: 262 * 263 * Poll the system monitor device. 264 */ 265int 266sysmonpoll(dev_t dev, int events, struct lwp *l) 267{ 268 int rv; 269 270 switch (minor(dev)) { 271 case SYSMON_MINOR_POWER: 272 if (sysmon_opvec_table[minor(dev)] == NULL) 273 rv = events; 274 else 275 rv = (sysmon_opvec_table[minor(dev)]->so_poll)(dev, 276 events, l); 277 break; 278 default: 279 rv = events; 280 } 281 282 return (rv); 283} 284 285/* 286 * sysmonkqfilter: 287 * 288 * Kqueue filter for the system monitor device. 289 */ 290int 291sysmonkqfilter(dev_t dev, struct knote *kn) 292{ 293 int error; 294 295 switch (minor(dev)) { 296 case SYSMON_MINOR_POWER: 297 if (sysmon_opvec_table[minor(dev)] == NULL) 298 error = ENODEV; 299 else 300 error = (sysmon_opvec_table[minor(dev)]->so_filter)(dev, 301 kn); 302 break; 303 default: 304 error = 1; 305 } 306 307 return (error); 308} 309 310MODULE(MODULE_CLASS_DRIVER, sysmon, NULL); 311 312static int 313sm_init_once(void) 314{ 315 316 mutex_init(&sysmon_minor_mtx, MUTEX_DEFAULT, IPL_NONE); 317 318 return 0; 319} 320 321int 322sysmon_init(void) 323{ 324 int error; 325#ifdef _MODULE 326 devmajor_t bmajor, cmajor; 327#endif 328 329 error = RUN_ONCE(&once_sm, sm_init_once); 330 331#ifdef _MODULE 332 mutex_enter(&sysmon_minor_mtx); 333 if (!sm_is_attached) { 334 bmajor = cmajor = -1; 335 error = devsw_attach("sysmon", NULL, &bmajor, 336 &sysmon_cdevsw, &cmajor); 337 sm_is_attached = (error != 0); 338 } 339 mutex_exit(&sysmon_minor_mtx); 340#endif 341 342 return error; 343} 344 345int 346sysmon_fini(void) 347{ 348 int error = 0; 349 350 if ((sysmon_opvec_table[SYSMON_MINOR_ENVSYS] != NULL) || 351 (sysmon_opvec_table[SYSMON_MINOR_WDOG] != NULL) || 352 (sysmon_opvec_table[SYSMON_MINOR_POWER] != NULL)) 353 error = EBUSY; 354 355#ifdef _MODULE 356 if (error == 0) { 357 mutex_enter(&sysmon_minor_mtx); 358 sm_is_attached = false; 359 devsw_detach(NULL, &sysmon_cdevsw); 360 mutex_exit(&sysmon_minor_mtx); 361 } 362#endif 363 364 return error; 365} 366 367static int 368sysmon_modcmd(modcmd_t cmd, void *arg) 369{ 370 int ret; 371 372 switch (cmd) { 373 case MODULE_CMD_INIT: 374 ret = sysmon_init(); 375 break; 376 case MODULE_CMD_FINI: 377 ret = sysmon_fini(); 378 break; 379 case MODULE_CMD_STAT: 380 default: 381 ret = ENOTTY; 382 } 383 384 return ret; 385} 386