1 /* $OpenBSD: kern_sensors.c,v 1.19 2007/06/04 18:42:05 deraadt Exp $ */
6 * Copyright (c) 2005 David Gwynne <dlg@openbsd.org>
7 * Copyright (c) 2006 Constantine A. Murenin <cnst+openbsd@bugmail.mojo.ru>
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.
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.
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>
30 #include <sys/spinlock.h>
31 #include <sys/spinlock2.h>
34 #include <sys/sysctl.h>
35 #include <sys/sensors.h>
37 #include <sys/mplock2.h>
39 static int sensordev_idmax
;
40 static TAILQ_HEAD(sensordev_list
, ksensordev
) sensordev_list
=
41 TAILQ_HEAD_INITIALIZER(sensordev_list
);
43 static struct ksensordev
*sensordev_get(int);
44 static struct ksensor
*sensor_find(struct ksensordev
*, enum sensor_type
,
52 time_t nextrun
; /* time_uptime */
54 TAILQ_ENTRY(sensor_task
) entry
;
57 static void sensor_task_thread(void *);
58 static void sensor_task_schedule(struct sensor_task
*);
60 static TAILQ_HEAD(, sensor_task
) sensor_tasklist
=
61 TAILQ_HEAD_INITIALIZER(sensor_tasklist
);
63 static struct lock sensor_task_lock
=
64 LOCK_INITIALIZER("ksensor_task", 0, LK_CANRECURSE
);
66 static void sensordev_sysctl_install(struct ksensordev
*);
67 static void sensordev_sysctl_deinstall(struct ksensordev
*);
68 static void sensor_sysctl_install(struct ksensordev
*,
70 static void sensor_sysctl_deinstall(struct ksensordev
*,
74 sensordev_install(struct ksensordev
*sensdev
)
76 struct ksensordev
*v
, *after
= NULL
;
81 TAILQ_FOREACH(v
, &sensordev_list
, list
) {
85 } else if (v
->num
> num
) {
92 KKASSERT(sensdev
->num
== 0);
93 TAILQ_INSERT_HEAD(&sensordev_list
, sensdev
, list
);
95 TAILQ_INSERT_AFTER(&sensordev_list
, after
, sensdev
, list
);
98 /* Save max sensor device id */
99 sensordev_idmax
= TAILQ_LAST(&sensordev_list
, sensordev_list
)->num
+ 1;
101 /* Install sysctl node for this sensor device */
102 sensordev_sysctl_install(sensdev
);
108 sensor_attach(struct ksensordev
*sensdev
, struct ksensor
*sens
)
110 struct ksensor
*v
, *nv
;
111 struct ksensors_head
*sh
;
116 sh
= &sensdev
->sensors_list
;
117 if (sensdev
->sensors_count
== 0) {
118 for (i
= 0; i
< SENSOR_MAX_TYPES
; i
++)
119 sensdev
->maxnumt
[i
] = 0;
121 SLIST_INSERT_HEAD(sh
, sens
, list
);
123 for (v
= SLIST_FIRST(sh
);
124 (nv
= SLIST_NEXT(v
, list
)) != NULL
; v
= nv
)
125 if (v
->type
== sens
->type
&& (v
->type
!= nv
->type
||
126 (v
->type
== nv
->type
&& nv
->numt
- v
->numt
> 1)))
128 /* sensors of the same type go after each other */
129 if (v
->type
== sens
->type
)
130 sens
->numt
= v
->numt
+ 1;
133 SLIST_INSERT_AFTER(v
, sens
, list
);
136 * We only increment maxnumt[] if the sensor was added
137 * to the last position of sensors of this type
139 if (sensdev
->maxnumt
[sens
->type
] == sens
->numt
)
140 sensdev
->maxnumt
[sens
->type
]++;
141 sensdev
->sensors_count
++;
143 /* Install sysctl node for this sensor */
144 sensor_sysctl_install(sensdev
, sens
);
150 sensordev_deinstall(struct ksensordev
*sensdev
)
152 struct ksensordev
*last
;
156 TAILQ_REMOVE(&sensordev_list
, sensdev
, list
);
158 /* Adjust max sensor device id */
159 last
= TAILQ_LAST(&sensordev_list
, sensordev_list
);
161 sensordev_idmax
= last
->num
+ 1;
166 * Deinstall sensor device's sysctl node; this also
167 * removes all attached sensors' sysctl nodes.
169 sensordev_sysctl_deinstall(sensdev
);
175 sensor_detach(struct ksensordev
*sensdev
, struct ksensor
*sens
)
177 struct ksensors_head
*sh
;
181 sh
= &sensdev
->sensors_list
;
182 sensdev
->sensors_count
--;
183 SLIST_REMOVE(sh
, sens
, ksensor
, list
);
185 * We only decrement maxnumt[] if this is the tail
186 * sensor of this type
188 if (sens
->numt
== sensdev
->maxnumt
[sens
->type
] - 1)
189 sensdev
->maxnumt
[sens
->type
]--;
191 /* Deinstall sensor's sysctl node */
192 sensor_sysctl_deinstall(sensdev
, sens
);
197 static struct ksensordev
*
198 sensordev_get(int num
)
200 struct ksensordev
*sd
;
202 SYSCTL_ASSERT_XLOCKED();
204 TAILQ_FOREACH(sd
, &sensordev_list
, list
) {
211 static struct ksensor
*
212 sensor_find(struct ksensordev
*sensdev
, enum sensor_type type
, int numt
)
215 struct ksensors_head
*sh
;
217 SYSCTL_ASSERT_XLOCKED();
219 sh
= &sensdev
->sensors_list
;
220 SLIST_FOREACH(s
, sh
, list
) {
221 if (s
->type
== type
&& s
->numt
== numt
)
228 sensor_task_register(void *arg
, void (*func
)(void *), int period
)
230 struct sensor_task
*st
;
232 st
= kmalloc(sizeof(struct sensor_task
), M_DEVBUF
, M_WAITOK
);
234 lockmgr(&sensor_task_lock
, LK_EXCLUSIVE
);
242 TAILQ_INSERT_HEAD(&sensor_tasklist
, st
, entry
);
244 wakeup(&sensor_tasklist
);
246 lockmgr(&sensor_task_lock
, LK_RELEASE
);
250 sensor_task_unregister(void *arg
)
252 struct sensor_task
*st
;
254 lockmgr(&sensor_task_lock
, LK_EXCLUSIVE
);
255 TAILQ_FOREACH(st
, &sensor_tasklist
, entry
)
258 lockmgr(&sensor_task_lock
, LK_RELEASE
);
262 sensor_task_thread(void *arg
)
264 struct sensor_task
*st
, *nst
;
267 lockmgr(&sensor_task_lock
, LK_EXCLUSIVE
);
270 while (TAILQ_EMPTY(&sensor_tasklist
)) {
271 lksleep(&sensor_tasklist
, &sensor_task_lock
, 0,
275 while ((nst
= TAILQ_FIRST(&sensor_tasklist
))->nextrun
>
276 (now
= time_uptime
)) {
277 lksleep(&sensor_tasklist
, &sensor_task_lock
, 0,
278 "timeout", (nst
->nextrun
- now
) * hz
);
281 while ((st
= nst
) != NULL
) {
282 nst
= TAILQ_NEXT(st
, entry
);
284 if (st
->nextrun
> now
)
287 /* take it out while we work on it */
288 TAILQ_REMOVE(&sensor_tasklist
, st
, entry
);
297 /* stick it back in the tasklist */
298 sensor_task_schedule(st
);
302 lockmgr(&sensor_task_lock
, LK_RELEASE
);
306 sensor_task_schedule(struct sensor_task
*st
)
308 struct sensor_task
*cst
;
310 KASSERT(lockstatus(&sensor_task_lock
, curthread
) == LK_EXCLUSIVE
,
311 ("sensor task lock is not held"));
313 st
->nextrun
= time_uptime
+ st
->period
;
315 TAILQ_FOREACH(cst
, &sensor_tasklist
, entry
) {
316 if (cst
->nextrun
> st
->nextrun
) {
317 TAILQ_INSERT_BEFORE(cst
, st
, entry
);
322 /* must be an empty list, or at the end of the list */
323 TAILQ_INSERT_TAIL(&sensor_tasklist
, st
, entry
);
329 static int sysctl_handle_sensordev(SYSCTL_HANDLER_ARGS
);
330 static int sysctl_handle_sensor(SYSCTL_HANDLER_ARGS
);
331 static int sysctl_sensors_handler(SYSCTL_HANDLER_ARGS
);
333 SYSCTL_NODE(_hw
, OID_AUTO
, sensors
, CTLFLAG_RD
, NULL
,
334 "Hardware Sensors sysctl internal magic");
335 SYSCTL_NODE(_hw
, HW_SENSORS
, _sensors
, CTLFLAG_RD
, sysctl_sensors_handler
,
336 "Hardware Sensors XP MIB interface");
338 SYSCTL_INT(_hw_sensors
, OID_AUTO
, dev_idmax
, CTLFLAG_RD
,
339 &sensordev_idmax
, 0, "Max sensor device id");
342 sensordev_sysctl_install(struct ksensordev
*sensdev
)
344 struct sysctl_ctx_list
*cl
= &sensdev
->clist
;
346 struct ksensors_head
*sh
= &sensdev
->sensors_list
;
348 SYSCTL_ASSERT_XLOCKED();
350 KASSERT(sensdev
->oid
== NULL
,
351 ("sensor device %s sysctl node already installed", sensdev
->xname
));
354 sensdev
->oid
= SYSCTL_ADD_NODE(cl
, SYSCTL_STATIC_CHILDREN(_hw_sensors
),
355 sensdev
->num
, sensdev
->xname
, CTLFLAG_RD
, NULL
, "");
356 if (sensdev
->oid
== NULL
) {
357 kprintf("sensor: add sysctl tree for %s failed\n",
362 /* Install sysctl nodes for sensors attached to this sensor device */
363 SLIST_FOREACH(s
, sh
, list
)
364 sensor_sysctl_install(sensdev
, s
);
368 sensor_sysctl_install(struct ksensordev
*sensdev
, struct ksensor
*sens
)
372 SYSCTL_ASSERT_XLOCKED();
374 if (sensdev
->oid
== NULL
) {
375 /* Sensor device sysctl node is not installed yet */
379 ksnprintf(n
, sizeof(n
), "%s%d", sensor_type_s
[sens
->type
], sens
->numt
);
380 KASSERT(sens
->oid
== NULL
,
381 ("sensor %s:%s sysctl node already installed", sensdev
->xname
, n
));
383 sens
->oid
= SYSCTL_ADD_PROC(&sensdev
->clist
,
384 SYSCTL_CHILDREN(sensdev
->oid
), OID_AUTO
, n
,
385 CTLTYPE_STRUCT
| CTLFLAG_RD
, sens
, 0, sysctl_handle_sensor
,
390 sensordev_sysctl_deinstall(struct ksensordev
*sensdev
)
392 SYSCTL_ASSERT_XLOCKED();
394 if (sensdev
->oid
!= NULL
) {
395 sysctl_ctx_free(&sensdev
->clist
);
401 sensor_sysctl_deinstall(struct ksensordev
*sensdev
, struct ksensor
*sens
)
403 SYSCTL_ASSERT_XLOCKED();
405 if (sensdev
->oid
!= NULL
&& sens
->oid
!= NULL
) {
406 sysctl_ctx_entry_del(&sensdev
->clist
, sens
->oid
);
407 sysctl_remove_oid(sens
->oid
, 1, 0);
413 sysctl_handle_sensordev(SYSCTL_HANDLER_ARGS
)
415 struct ksensordev
*ksd
= arg1
;
416 struct sensordev
*usd
;
422 /* Grab a copy, to clear the kernel pointers */
423 usd
= kmalloc(sizeof(*usd
), M_TEMP
, M_WAITOK
| M_ZERO
);
425 strlcpy(usd
->xname
, ksd
->xname
, sizeof(usd
->xname
));
426 memcpy(usd
->maxnumt
, ksd
->maxnumt
, sizeof(usd
->maxnumt
));
427 usd
->sensors_count
= ksd
->sensors_count
;
429 error
= SYSCTL_OUT(req
, usd
, sizeof(struct sensordev
));
436 sysctl_handle_sensor(SYSCTL_HANDLER_ARGS
)
438 struct ksensor
*ks
= arg1
;
445 /* Grab a copy, to clear the kernel pointers */
446 us
= kmalloc(sizeof(*us
), M_TEMP
, M_WAITOK
| M_ZERO
);
447 memcpy(us
->desc
, ks
->desc
, sizeof(ks
->desc
));
449 us
->value
= ks
->value
;
451 us
->status
= ks
->status
;
453 us
->flags
= ks
->flags
;
455 error
= SYSCTL_OUT(req
, us
, sizeof(struct sensor
));
462 sysctl_sensors_handler(SYSCTL_HANDLER_ARGS
)
465 u_int namelen
= arg2
;
466 struct ksensordev
*ksd
;
469 enum sensor_type type
;
471 if (namelen
!= 1 && namelen
!= 3)
475 if ((ksd
= sensordev_get(dev
)) == NULL
)
479 return (sysctl_handle_sensordev(NULL
, ksd
, 0, req
));
484 if ((ks
= sensor_find(ksd
, type
, numt
)) == NULL
)
486 return (sysctl_handle_sensor(NULL
, ks
, 0, req
));
490 sensor_sysinit(void *arg __unused
)
494 error
= kthread_create(sensor_task_thread
, NULL
, NULL
, "sensors");
496 panic("sensors kthread failed: %d", error
);
498 SYSINIT(sensor
, SI_SUB_PRE_DRIVERS
, SI_ORDER_ANY
, sensor_sysinit
, NULL
);