sysmon.c revision 1.24 1 /* $NetBSD: sysmon.c,v 1.24 2015/04/25 23:40:09 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.24 2015/04/25 23:40:09 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 #ifdef _MODULE
349 devmajor_t bmajor, cmajor;
350 #endif
351 static struct cfdata cf;
352 int error = 0;
353
354 if (sysmon_dev != NULL) {
355 return EEXIST;
356 }
357
358 error = config_cfdriver_attach(&sysmon_cd);
359 if (error) {
360 aprint_error("%s: unable to attach cfdriver\n",
361 sysmon_cd.cd_name);
362 return error;
363 }
364 error = config_cfattach_attach(sysmon_cd.cd_name, &sysmon_ca);
365 if (error) {
366 config_cfdriver_detach(&sysmon_cd);
367 aprint_error("%s: unable to attach cfattach\n",
368 sysmon_cd.cd_name);
369 return error;
370 }
371
372 #ifdef _MODULE
373 bmajor = cmajor = -1;
374 error = devsw_attach("sysmon", NULL, &bmajor,
375 &sysmon_cdevsw, &cmajor);
376 if (error) {
377 config_cfattach_detach(sysmon_cd.cd_name, &sysmon_ca);
378 config_cfdriver_detach(&sysmon_cd);
379 aprint_error("%s: unable to attach devsw\n",
380 sysmon_cd.cd_name);
381 return error;
382 }
383 #endif
384
385 cf.cf_name = sysmon_cd.cd_name;
386 cf.cf_atname = sysmon_cd.cd_name;
387 cf.cf_unit = 0;
388 cf.cf_fstate = FSTATE_STAR;
389 cf.cf_pspec = NULL;
390 cf.cf_loc = NULL;
391 cf.cf_flags = 0;
392
393 sysmon_dev = config_attach_pseudo(&cf);
394 if (sysmon_dev == NULL) {
395 aprint_error("%s: failed to attach pseudo device\n",
396 sysmon_cd.cd_name);
397 error = ENODEV;
398 }
399
400 if (!pmf_device_register(sysmon_dev, NULL, NULL))
401 aprint_error("%s: failed to register with pmf\n",
402 sysmon_cd.cd_name);
403
404 return error;
405 }
406
407 int
408 sysmon_fini(void)
409 {
410 int error = 0;
411
412 if (sysmon_opvec_table[SYSMON_MINOR_ENVSYS] != NULL)
413 error = EBUSY;
414 else if (sysmon_opvec_table[SYSMON_MINOR_WDOG] != NULL)
415 error = EBUSY;
416 else if (sysmon_opvec_table[SYSMON_MINOR_POWER] != NULL)
417 error = EBUSY;
418
419 else {
420 pmf_device_deregister(sysmon_dev);
421 config_detach(sysmon_dev, 0);
422 devsw_detach(NULL, &sysmon_cdevsw);
423 config_cfattach_detach(sysmon_cd.cd_name, &sysmon_ca);
424 config_cfdriver_detach(&sysmon_cd);
425 }
426 if (error == 0)
427 sysmon_dev = NULL;
428
429 return error;
430 }
431
432 static
433 int
434 sysmon_modcmd(modcmd_t cmd, void *arg)
435 {
436 int ret;
437
438 switch (cmd) {
439 case MODULE_CMD_INIT:
440 ret = sysmon_init();
441 break;
442
443 case MODULE_CMD_FINI:
444 ret = sysmon_fini();
445 break;
446
447 case MODULE_CMD_STAT:
448 default:
449 ret = ENOTTY;
450 }
451
452 return ret;
453 }
454