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