xref: /dragonfly/sys/kern/kern_sensors.c (revision 62f7f702)
1 /* $OpenBSD: kern_sensors.c,v 1.19 2007/06/04 18:42:05 deraadt Exp $ */
2 /* $DragonFly: src/sys/kern/kern_sensors.c,v 1.2 2008/01/05 14:02:38 swildner Exp $ */
3 
4 /*
5  * Copyright (c) 2005 David Gwynne <dlg@openbsd.org>
6  * Copyright (c) 2006 Constantine A. Murenin <cnst+openbsd@bugmail.mojo.ru>
7  *
8  * Permission to use, copy, modify, and distribute this software for any
9  * purpose with or without fee is hereby granted, provided that the above
10  * copyright notice and this permission notice appear in all copies.
11  *
12  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19  */
20 
21 #include <sys/cdefs.h>
22 #include <sys/param.h>
23 #include <sys/systm.h>
24 #include <sys/kernel.h>
25 #include <sys/malloc.h>
26 #include <sys/kthread.h>
27 #include <sys/queue.h>
28 #include <sys/types.h>
29 #include <sys/time.h>
30 #include <sys/lock.h>
31 
32 #include <sys/sysctl.h>
33 #include <sys/sensors.h>
34 
35 int			sensordev_count = 0;
36 SLIST_HEAD(, ksensordev) sensordev_list = SLIST_HEAD_INITIALIZER(sensordev_list);
37 
38 struct ksensordev	*sensordev_get(int);
39 struct ksensor		*sensor_find(struct ksensordev *, enum sensor_type, int);
40 
41 struct sensor_task {
42 	void				*arg;
43 	void				(*func)(void *);
44 
45 	int				period;
46 	time_t				nextrun;
47 	volatile int			running;
48 	TAILQ_ENTRY(sensor_task)	entry;
49 };
50 
51 void	sensor_task_thread(void *);
52 void	sensor_task_schedule(struct sensor_task *);
53 
54 TAILQ_HEAD(, sensor_task) tasklist = TAILQ_HEAD_INITIALIZER(tasklist);
55 
56 #ifndef NOSYSCTL8HACK
57 void	sensor_sysctl8magic_install(struct ksensordev *);
58 void	sensor_sysctl8magic_deinstall(struct ksensordev *);
59 #endif
60 
61 void
62 sensordev_install(struct ksensordev *sensdev)
63 {
64 	struct ksensordev *v, *nv;
65 
66 	/* mtx_lock(&Giant); */
67 	if (sensordev_count == 0) {
68 		sensdev->num = 0;
69 		SLIST_INSERT_HEAD(&sensordev_list, sensdev, list);
70 	} else {
71 		for (v = SLIST_FIRST(&sensordev_list);
72 		    (nv = SLIST_NEXT(v, list)) != NULL; v = nv)
73 			if (nv->num - v->num > 1)
74 				break;
75 		sensdev->num = v->num + 1;
76 		SLIST_INSERT_AFTER(v, sensdev, list);
77 	}
78 	sensordev_count++;
79 	/* mtx_unlock(&Giant); */
80 
81 #ifndef NOSYSCTL8HACK
82 	sensor_sysctl8magic_install(sensdev);
83 #endif
84 }
85 
86 void
87 sensor_attach(struct ksensordev *sensdev, struct ksensor *sens)
88 {
89 	struct ksensor *v, *nv;
90 	struct ksensors_head *sh;
91 	int i;
92 
93 	/* mtx_lock(&Giant); */
94 	sh = &sensdev->sensors_list;
95 	if (sensdev->sensors_count == 0) {
96 		for (i = 0; i < SENSOR_MAX_TYPES; i++)
97 			sensdev->maxnumt[i] = 0;
98 		sens->numt = 0;
99 		SLIST_INSERT_HEAD(sh, sens, list);
100 	} else {
101 		for (v = SLIST_FIRST(sh);
102 		    (nv = SLIST_NEXT(v, list)) != NULL; v = nv)
103 			if (v->type == sens->type && (v->type != nv->type ||
104 			    (v->type == nv->type && nv->numt - v->numt > 1)))
105 				break;
106 		/* sensors of the same type go after each other */
107 		if (v->type == sens->type)
108 			sens->numt = v->numt + 1;
109 		else
110 			sens->numt = 0;
111 		SLIST_INSERT_AFTER(v, sens, list);
112 	}
113 	/* we only increment maxnumt[] if the sensor was added
114 	 * to the last position of sensors of this type
115 	 */
116 	if (sensdev->maxnumt[sens->type] == sens->numt)
117 		sensdev->maxnumt[sens->type]++;
118 	sensdev->sensors_count++;
119 	/* mtx_unlock(&Giant); */
120 }
121 
122 void
123 sensordev_deinstall(struct ksensordev *sensdev)
124 {
125 	/* mtx_lock(&Giant); */
126 	sensordev_count--;
127 	SLIST_REMOVE(&sensordev_list, sensdev, ksensordev, list);
128 	/* mtx_unlock(&Giant); */
129 
130 #ifndef NOSYSCTL8HACK
131 	sensor_sysctl8magic_deinstall(sensdev);
132 #endif
133 }
134 
135 void
136 sensor_detach(struct ksensordev *sensdev, struct ksensor *sens)
137 {
138 	struct ksensors_head *sh;
139 
140 	/* mtx_lock(&Giant); */
141 	sh = &sensdev->sensors_list;
142 	sensdev->sensors_count--;
143 	SLIST_REMOVE(sh, sens, ksensor, list);
144 	/* we only decrement maxnumt[] if this is the tail
145 	 * sensor of this type
146 	 */
147 	if (sens->numt == sensdev->maxnumt[sens->type] - 1)
148 		sensdev->maxnumt[sens->type]--;
149 	/* mtx_unlock(&Giant); */
150 }
151 
152 struct ksensordev *
153 sensordev_get(int num)
154 {
155 	struct ksensordev *sd;
156 
157 	SLIST_FOREACH(sd, &sensordev_list, list)
158 		if (sd->num == num)
159 			return (sd);
160 
161 	return (NULL);
162 }
163 
164 struct ksensor *
165 sensor_find(struct ksensordev *sensdev, enum sensor_type type, int numt)
166 {
167 	struct ksensor *s;
168 	struct ksensors_head *sh;
169 
170 	sh = &sensdev->sensors_list;
171 	SLIST_FOREACH(s, sh, list)
172 		if (s->type == type && s->numt == numt)
173 			return (s);
174 
175 	return (NULL);
176 }
177 
178 int
179 sensor_task_register(void *arg, void (*func)(void *), int period)
180 {
181 	struct sensor_task	*st;
182 	int			 create_thread = 0;
183 
184 	st = kmalloc(sizeof(struct sensor_task), M_DEVBUF, M_NOWAIT);
185 	if (st == NULL)
186 		return (1);
187 
188 	st->arg = arg;
189 	st->func = func;
190 	st->period = period;
191 
192 	st->running = 1;
193 
194 	if (TAILQ_EMPTY(&tasklist))
195 		create_thread = 1;
196 
197 	st->nextrun = 0;
198 	TAILQ_INSERT_HEAD(&tasklist, st, entry);
199 
200 	if (create_thread)
201 		if (kthread_create(sensor_task_thread, NULL, NULL, 0, 0,
202 		    "sensors") != 0)
203 			panic("sensors kthread");
204 
205 	wakeup(&tasklist);
206 
207 	return (0);
208 }
209 
210 void
211 sensor_task_unregister(void *arg)
212 {
213 	struct sensor_task	*st;
214 
215 	TAILQ_FOREACH(st, &tasklist, entry)
216 		if (st->arg == arg)
217 			st->running = 0;
218 }
219 
220 void
221 sensor_task_thread(void *arg)
222 {
223 	struct sensor_task	*st, *nst;
224 	time_t			now;
225 
226 	while (!TAILQ_EMPTY(&tasklist)) {
227 		while ((nst = TAILQ_FIRST(&tasklist))->nextrun >
228 		    (now = time_second))
229 			tsleep(&tasklist, 0, "timeout",
230 			       (nst->nextrun - now) * hz);
231 
232 		while ((st = nst) != NULL) {
233 			nst = TAILQ_NEXT(st, entry);
234 
235 			if (st->nextrun > now)
236 				break;
237 
238 			/* take it out while we work on it */
239 			TAILQ_REMOVE(&tasklist, st, entry);
240 
241 			if (!st->running) {
242 				kfree(st, M_DEVBUF);
243 				continue;
244 			}
245 
246 			/* run the task */
247 			st->func(st->arg);
248 			/* stick it back in the tasklist */
249 			sensor_task_schedule(st);
250 		}
251 	}
252 
253 	kthread_exit();
254 }
255 
256 void
257 sensor_task_schedule(struct sensor_task *st)
258 {
259 	struct sensor_task 	*cst;
260 
261 	st->nextrun = time_second + st->period;
262 
263 	TAILQ_FOREACH(cst, &tasklist, entry) {
264 		if (cst->nextrun > st->nextrun) {
265 			TAILQ_INSERT_BEFORE(cst, st, entry);
266 			return;
267 		}
268 	}
269 
270 	/* must be an empty list, or at the end of the list */
271 	TAILQ_INSERT_TAIL(&tasklist, st, entry);
272 }
273 
274 /*
275  * sysctl glue code
276  */
277 int sysctl_handle_sensordev(SYSCTL_HANDLER_ARGS);
278 int sysctl_handle_sensor(SYSCTL_HANDLER_ARGS);
279 int sysctl_sensors_handler(SYSCTL_HANDLER_ARGS);
280 
281 #ifndef NOSYSCTL8HACK
282 
283 SYSCTL_NODE(_hw, OID_AUTO, sensors, CTLFLAG_RD, NULL,
284     "Hardware Sensors sysctl internal magic");
285 SYSCTL_NODE(_hw, HW_SENSORS, _sensors, CTLFLAG_RD, sysctl_sensors_handler,
286     "Hardware Sensors XP MIB interface");
287 
288 #else /* NOSYSCTL8HACK */
289 
290 SYSCTL_NODE(_hw, HW_SENSORS, sensors, CTLFLAG_RD, sysctl_sensors_handler,
291     "Hardware Sensors");
292 int sensors_debug = 1;
293 SYSCTL_INT(_hw_sensors, OID_AUTO, debug, CTLFLAG_RD, &sensors_debug, 0, "sensors debug");
294 
295 #endif /* !NOSYSCTL8HACK */
296 
297 
298 #ifndef NOSYSCTL8HACK
299 
300 /*
301  * XXX:
302  * FreeBSD's sysctl(9) .oid_handler functionality is not accustomed
303  * for the CTLTYPE_NODE handler to handle the undocumented sysctl
304  * magic calls.  As soon as such functionality is developed,
305  * sysctl_sensors_handler() should be converted to handle all such
306  * calls, and these sysctl_add_oid(9) calls should be removed
307  * "with a big axe".  This whole sysctl_add_oid(9) business is solely
308  * to please sysctl(8).
309  */
310 
311 void
312 sensor_sysctl8magic_install(struct ksensordev *sensdev)
313 {
314 	struct sysctl_oid_list *ol;
315 	struct sysctl_ctx_list *cl = &sensdev->clist;
316 	struct ksensor *s;
317 	struct ksensors_head *sh = &sensdev->sensors_list;
318 
319 	sysctl_ctx_init(cl);
320 	ol = SYSCTL_CHILDREN(SYSCTL_ADD_NODE(cl, (&SYSCTL_NODE_CHILDREN(_hw,
321 	    sensors)), sensdev->num, sensdev->xname, CTLFLAG_RD, NULL, ""));
322 	SLIST_FOREACH(s, sh, list) {
323 		char n[32];
324 
325 		ksnprintf(n, sizeof(n), "%s%d", sensor_type_s[s->type], s->numt);
326 		SYSCTL_ADD_PROC(cl, ol, OID_AUTO, n, CTLTYPE_STRUCT |
327 		    CTLFLAG_RD, s, 0, sysctl_handle_sensor, "S,sensor", "");
328 	}
329 }
330 
331 void
332 sensor_sysctl8magic_deinstall(struct ksensordev *sensdev)
333 {
334 	struct sysctl_ctx_list *cl = &sensdev->clist;
335 
336 	sysctl_ctx_free(cl);
337 }
338 
339 #endif /* !NOSYSCTL8HACK */
340 
341 
342 int
343 sysctl_handle_sensordev(SYSCTL_HANDLER_ARGS)
344 {
345 	struct ksensordev *ksd = arg1;
346 	struct sensordev *usd;
347 	int error;
348 
349 	if (req->newptr)
350 		return (EPERM);
351 
352 	/* Grab a copy, to clear the kernel pointers */
353 	usd = kmalloc(sizeof(*usd), M_TEMP, M_WAITOK | M_ZERO);
354 	usd->num = ksd->num;
355 	strlcpy(usd->xname, ksd->xname, sizeof(usd->xname));
356 	memcpy(usd->maxnumt, ksd->maxnumt, sizeof(usd->maxnumt));
357 	usd->sensors_count = ksd->sensors_count;
358 
359 	error = SYSCTL_OUT(req, usd, sizeof(struct sensordev));
360 
361 	kfree(usd, M_TEMP);
362 	return (error);
363 
364 }
365 
366 int
367 sysctl_handle_sensor(SYSCTL_HANDLER_ARGS)
368 {
369 	struct ksensor *ks = arg1;
370 	struct sensor *us;
371 	int error;
372 
373 	if (req->newptr)
374 		return (EPERM);
375 
376 	/* Grab a copy, to clear the kernel pointers */
377 	us = kmalloc(sizeof(*us), M_TEMP, M_WAITOK | M_ZERO);
378 	memcpy(us->desc, ks->desc, sizeof(ks->desc));
379 	us->tv = ks->tv;
380 	us->value = ks->value;
381 	us->type = ks->type;
382 	us->status = ks->status;
383 	us->numt = ks->numt;
384 	us->flags = ks->flags;
385 
386 	error = SYSCTL_OUT(req, us, sizeof(struct sensor));
387 
388 	kfree(us, M_TEMP);
389 	return (error);
390 }
391 
392 int
393 sysctl_sensors_handler(SYSCTL_HANDLER_ARGS)
394 {
395 	int *name = arg1;
396 	u_int namelen = arg2;
397 	struct ksensordev *ksd;
398 	struct ksensor *ks;
399 	int dev, numt;
400 	enum sensor_type type;
401 
402 	if (namelen != 1 && namelen != 3)
403 		return (ENOTDIR);
404 
405 	dev = name[0];
406 	if ((ksd = sensordev_get(dev)) == NULL)
407 		return (ENOENT);
408 
409 	if (namelen == 1)
410 		return (sysctl_handle_sensordev(NULL, ksd, 0, req));
411 
412 	type = name[1];
413 	numt = name[2];
414 
415 	if ((ks = sensor_find(ksd, type, numt)) == NULL)
416 		return (ENOENT);
417 	return (sysctl_handle_sensor(NULL, ks, 0, req));
418 }
419