sysmon.c revision 1.25 1 /* $NetBSD: sysmon.c,v 1.25 2015/04/29 03:27:27 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.25 2015/04/29 03:27:27 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 #include <sys/once.h>
56
57 #include <dev/sysmon/sysmonvar.h>
58
59 dev_type_open(sysmonopen);
60 dev_type_close(sysmonclose);
61 dev_type_ioctl(sysmonioctl);
62 dev_type_read(sysmonread);
63 dev_type_poll(sysmonpoll);
64 dev_type_kqfilter(sysmonkqfilter);
65
66 const 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
81 static int sysmon_modcmd(modcmd_t, void *);
82 static int sm_init_once(void);
83
84 /*
85 * Info about our minor "devices"
86 */
87 static struct sysmon_opvec *sysmon_opvec_table[] = { NULL, NULL, NULL };
88 static int sysmon_refcnt[] = { 0, 0, 0 };
89 static const char *sysmon_mod[] = { "sysmon_envsys",
90 "sysmon_wdog",
91 "sysmon_power" };
92 static kmutex_t sysmon_minor_mtx;
93
94 #ifdef _MODULE
95 static bool sm_is_attached;
96 #endif
97
98 ONCE_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 */
110 int
111 sysmon_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 */
140 int
141 sysmonopen(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_MISC);
155 mutex_enter(&sysmon_minor_mtx);
156 if (sysmon_opvec_table[minor(dev)] == NULL)
157 error = ENODEV;
158 }
159 error = (sysmon_opvec_table[minor(dev)]->so_open)(dev, flag,
160 mode, l);
161 if (error == 0)
162 sysmon_refcnt[minor(dev)]++;
163 break;
164 default:
165 error = ENODEV;
166 }
167
168 mutex_exit(&sysmon_minor_mtx);
169 return (error);
170 }
171
172 /*
173 * sysmonclose:
174 *
175 * Close the system monitor device.
176 */
177 int
178 sysmonclose(dev_t dev, int flag, int mode, struct lwp *l)
179 {
180 int error;
181
182 switch (minor(dev)) {
183 case SYSMON_MINOR_ENVSYS:
184 case SYSMON_MINOR_WDOG:
185 case SYSMON_MINOR_POWER:
186 if (sysmon_opvec_table[minor(dev)] == NULL)
187 error = ENODEV;
188 else {
189 error = (sysmon_opvec_table[minor(dev)]->so_close)(dev,
190 flag, mode, l);
191 if (error == 0) {
192 sysmon_refcnt[minor(dev)]--;
193 KASSERT(sysmon_refcnt[minor(dev)] >= 0);
194 }
195 }
196 break;
197 default:
198 error = ENODEV;
199 }
200
201 return (error);
202 }
203
204 /*
205 * sysmonioctl:
206 *
207 * Perform a control request.
208 */
209 int
210 sysmonioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
211 {
212 int error;
213
214 switch (minor(dev)) {
215 case SYSMON_MINOR_ENVSYS:
216 case SYSMON_MINOR_WDOG:
217 case SYSMON_MINOR_POWER:
218 if (sysmon_opvec_table[minor(dev)] == NULL)
219 error = ENODEV;
220 else
221 error = (sysmon_opvec_table[minor(dev)]->so_ioctl)(dev,
222 cmd, data, flag, l);
223 break;
224 default:
225 error = ENODEV;
226 }
227
228 return (error);
229 }
230
231 /*
232 * sysmonread:
233 *
234 * Perform a read request.
235 */
236 int
237 sysmonread(dev_t dev, struct uio *uio, int flags)
238 {
239 int error;
240
241 switch (minor(dev)) {
242 case SYSMON_MINOR_POWER:
243 if (sysmon_opvec_table[minor(dev)] == NULL)
244 error = ENODEV;
245 else
246 error = (sysmon_opvec_table[minor(dev)]->so_read)(dev,
247 uio, flags);
248 break;
249 default:
250 error = ENODEV;
251 }
252
253 return (error);
254 }
255
256 /*
257 * sysmonpoll:
258 *
259 * Poll the system monitor device.
260 */
261 int
262 sysmonpoll(dev_t dev, int events, struct lwp *l)
263 {
264 int rv;
265
266 switch (minor(dev)) {
267 case SYSMON_MINOR_POWER:
268 if (sysmon_opvec_table[minor(dev)] == NULL)
269 rv = events;
270 else
271 rv = (sysmon_opvec_table[minor(dev)]->so_poll)(dev,
272 events, l);
273 break;
274 default:
275 rv = events;
276 }
277
278 return (rv);
279 }
280
281 /*
282 * sysmonkqfilter:
283 *
284 * Kqueue filter for the system monitor device.
285 */
286 int
287 sysmonkqfilter(dev_t dev, struct knote *kn)
288 {
289 int error;
290
291 switch (minor(dev)) {
292 case SYSMON_MINOR_POWER:
293 if (sysmon_opvec_table[minor(dev)] == NULL)
294 error = ENODEV;
295 else
296 error = (sysmon_opvec_table[minor(dev)]->so_filter)(dev,
297 kn);
298 break;
299 default:
300 error = 1;
301 }
302
303 return (error);
304 }
305
306 MODULE(MODULE_CLASS_DRIVER, sysmon, "");
307
308 static int
309 sm_init_once(void)
310 {
311
312 mutex_init(&sysmon_minor_mtx, MUTEX_DEFAULT, IPL_NONE);
313
314 return 0;
315 }
316
317 int
318 sysmon_init(void)
319 {
320 int error;
321 #ifdef _MODULE
322 devmajor_t bmajor, cmajor;
323 #endif
324
325 error = RUN_ONCE(&once_sm, sm_init_once);
326
327 #ifdef _MODULE
328 mutex_enter(&sysmon_minor_mtx);
329 if (!sm_is_attached) {
330 bmajor = cmajor = -1;
331 error = devsw_attach("sysmon", NULL, &bmajor,
332 &sysmon_cdevsw, &cmajor);
333 sm_is_attached = (error != 0);
334 }
335 mutex_exit(&sysmon_minor_mtx);
336 #endif
337
338 return error;
339 }
340
341 int
342 sysmon_fini(void)
343 {
344 int error = 0;
345
346 if ((sysmon_opvec_table[SYSMON_MINOR_ENVSYS] != NULL) ||
347 (sysmon_opvec_table[SYSMON_MINOR_WDOG] != NULL) ||
348 (sysmon_opvec_table[SYSMON_MINOR_POWER] != NULL))
349 error = EBUSY;
350
351 #ifdef _MODULE
352 if (error == 0) {
353 mutex_enter(&sysmon_minor_mtx);
354 sm_is_attached = false;
355 error = devsw_detach(NULL, &sysmon_cdevsw);
356 mutex_exit(&sysmon_minor_mtx);
357 }
358 #endif
359
360 return error;
361 }
362
363 static
364 int
365 sysmon_modcmd(modcmd_t cmd, void *arg)
366 {
367 int ret;
368
369 switch (cmd) {
370 case MODULE_CMD_INIT:
371 ret = sysmon_init();
372 break;
373
374 case MODULE_CMD_FINI:
375 ret = sysmon_fini();
376 break;
377
378 case MODULE_CMD_STAT:
379 default:
380 ret = ENOTTY;
381 }
382
383 return ret;
384 }
385