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