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 sensor_task_lock_inited
= 0;
40 static struct lock sensor_task_lock
;
41 static struct spinlock sensor_dev_lock
= SPINLOCK_INITIALIZER(sensor_dev_lock
);
43 int sensordev_count
= 0;
44 SLIST_HEAD(, ksensordev
) sensordev_list
= SLIST_HEAD_INITIALIZER(sensordev_list
);
46 struct ksensordev
*sensordev_get(int);
47 struct ksensor
*sensor_find(struct ksensordev
*, enum sensor_type
, int);
56 TAILQ_ENTRY(sensor_task
) entry
;
59 void sensor_task_thread(void *);
60 void sensor_task_schedule(struct sensor_task
*);
62 TAILQ_HEAD(, sensor_task
) tasklist
= TAILQ_HEAD_INITIALIZER(tasklist
);
65 void sensor_sysctl8magic_install(struct ksensordev
*);
66 void sensor_sysctl8magic_deinstall(struct ksensordev
*);
70 sensordev_install(struct ksensordev
*sensdev
)
72 struct ksensordev
*v
, *nv
;
74 /* mtx_lock(&Giant); */
75 spin_lock(&sensor_dev_lock
);
76 if (sensordev_count
== 0) {
78 SLIST_INSERT_HEAD(&sensordev_list
, sensdev
, list
);
80 for (v
= SLIST_FIRST(&sensordev_list
);
81 (nv
= SLIST_NEXT(v
, list
)) != NULL
; v
= nv
)
82 if (nv
->num
- v
->num
> 1)
84 sensdev
->num
= v
->num
+ 1;
85 SLIST_INSERT_AFTER(v
, sensdev
, list
);
88 /* mtx_unlock(&Giant); */
89 spin_unlock(&sensor_dev_lock
);
92 sensor_sysctl8magic_install(sensdev
);
97 sensor_attach(struct ksensordev
*sensdev
, struct ksensor
*sens
)
99 struct ksensor
*v
, *nv
;
100 struct ksensors_head
*sh
;
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;
110 SLIST_INSERT_HEAD(sh
, sens
, list
);
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)))
117 /* sensors of the same type go after each other */
118 if (v
->type
== sens
->type
)
119 sens
->numt
= v
->numt
+ 1;
122 SLIST_INSERT_AFTER(v
, sens
, list
);
124 /* we only increment maxnumt[] if the sensor was added
125 * to the last position of sensors of this type
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); */
135 sensordev_deinstall(struct ksensordev
*sensdev
)
137 /* mtx_lock(&Giant); */
138 spin_lock(&sensor_dev_lock
);
140 SLIST_REMOVE(&sensordev_list
, sensdev
, ksensordev
, list
);
141 /* mtx_unlock(&Giant); */
142 spin_unlock(&sensor_dev_lock
);
144 #ifndef NOSYSCTL8HACK
145 sensor_sysctl8magic_deinstall(sensdev
);
150 sensor_detach(struct ksensordev
*sensdev
, struct ksensor
*sens
)
152 struct ksensors_head
*sh
;
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
161 if (sens
->numt
== sensdev
->maxnumt
[sens
->type
] - 1)
162 sensdev
->maxnumt
[sens
->type
]--;
163 /* mtx_unlock(&Giant); */
167 sensordev_get(int num
)
169 struct ksensordev
*sd
;
171 spin_lock(&sensor_dev_lock
);
172 SLIST_FOREACH(sd
, &sensordev_list
, list
)
173 if (sd
->num
== num
) {
174 spin_unlock(&sensor_dev_lock
);
178 spin_unlock(&sensor_dev_lock
);
183 sensor_find(struct ksensordev
*sensdev
, enum sensor_type type
, int numt
)
186 struct ksensors_head
*sh
;
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
);
197 spin_unlock(&sensor_dev_lock
);
202 sensor_task_register(void *arg
, void (*func
)(void *), int period
)
204 struct sensor_task
*st
;
205 int create_thread
= 0;
207 st
= kmalloc(sizeof(struct sensor_task
), M_DEVBUF
, M_NOWAIT
);
211 if (atomic_cmpset_int(&sensor_task_lock_inited
, 0, 1)) {
212 lockinit(&sensor_task_lock
, "ksensor_task", 0, LK_CANRECURSE
);
215 lockmgr(&sensor_task_lock
, LK_EXCLUSIVE
);
222 if (TAILQ_EMPTY(&tasklist
))
226 TAILQ_INSERT_HEAD(&tasklist
, st
, entry
);
229 if (kthread_create(sensor_task_thread
, NULL
, NULL
,
231 panic("sensors kthread");
235 lockmgr(&sensor_task_lock
, LK_RELEASE
);
240 sensor_task_unregister(void *arg
)
242 struct sensor_task
*st
;
244 lockmgr(&sensor_task_lock
, LK_EXCLUSIVE
);
245 TAILQ_FOREACH(st
, &tasklist
, entry
)
248 lockmgr(&sensor_task_lock
, LK_RELEASE
);
252 sensor_task_thread(void *arg
)
254 struct sensor_task
*st
, *nst
;
257 lockmgr(&sensor_task_lock
, LK_EXCLUSIVE
);
259 while (!TAILQ_EMPTY(&tasklist
)) {
260 while ((nst
= TAILQ_FIRST(&tasklist
))->nextrun
>
262 lksleep(&tasklist
, &sensor_task_lock
, 0, "timeout",
263 (nst
->nextrun
- now
) * hz
);
265 while ((st
= nst
) != NULL
) {
266 nst
= TAILQ_NEXT(st
, entry
);
268 if (st
->nextrun
> now
)
271 /* take it out while we work on it */
272 TAILQ_REMOVE(&tasklist
, st
, entry
);
281 /* stick it back in the tasklist */
282 sensor_task_schedule(st
);
286 lockmgr(&sensor_task_lock
, LK_RELEASE
);
290 sensor_task_schedule(struct sensor_task
*st
)
292 struct sensor_task
*cst
;
294 lockmgr(&sensor_task_lock
, LK_EXCLUSIVE
);
295 st
->nextrun
= time_second
+ st
->period
;
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
);
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
);
313 int sysctl_handle_sensordev(SYSCTL_HANDLER_ARGS
);
314 int sysctl_handle_sensor(SYSCTL_HANDLER_ARGS
);
315 int sysctl_sensors_handler(SYSCTL_HANDLER_ARGS
);
317 #ifndef NOSYSCTL8HACK
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");
324 #else /* NOSYSCTL8HACK */
326 SYSCTL_NODE(_hw
, HW_SENSORS
, sensors
, CTLFLAG_RD
, sysctl_sensors_handler
,
328 int sensors_debug
= 1;
329 SYSCTL_INT(_hw_sensors
, OID_AUTO
, debug
, CTLFLAG_RD
, &sensors_debug
, 0, "sensors debug");
331 #endif /* !NOSYSCTL8HACK */
334 #ifndef NOSYSCTL8HACK
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).
348 sensor_sysctl8magic_install(struct ksensordev
*sensdev
)
350 struct sysctl_oid_list
*ol
;
351 struct sysctl_ctx_list
*cl
= &sensdev
->clist
;
353 struct ksensors_head
*sh
= &sensdev
->sensors_list
;
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
) {
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", "");
368 sensor_sysctl8magic_deinstall(struct ksensordev
*sensdev
)
370 struct sysctl_ctx_list
*cl
= &sensdev
->clist
;
375 #endif /* !NOSYSCTL8HACK */
379 sysctl_handle_sensordev(SYSCTL_HANDLER_ARGS
)
381 struct ksensordev
*ksd
= arg1
;
382 struct sensordev
*usd
;
388 /* Grab a copy, to clear the kernel pointers */
389 usd
= kmalloc(sizeof(*usd
), M_TEMP
, M_WAITOK
| M_ZERO
);
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
;
395 error
= SYSCTL_OUT(req
, usd
, sizeof(struct sensordev
));
403 sysctl_handle_sensor(SYSCTL_HANDLER_ARGS
)
405 struct ksensor
*ks
= arg1
;
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
));
416 us
->value
= ks
->value
;
418 us
->status
= ks
->status
;
420 us
->flags
= ks
->flags
;
422 error
= SYSCTL_OUT(req
, us
, sizeof(struct sensor
));
429 sysctl_sensors_handler(SYSCTL_HANDLER_ARGS
)
432 u_int namelen
= arg2
;
433 struct ksensordev
*ksd
;
436 enum sensor_type type
;
438 if (namelen
!= 1 && namelen
!= 3)
442 if ((ksd
= sensordev_get(dev
)) == NULL
)
446 return (sysctl_handle_sensordev(NULL
, ksd
, 0, req
));
451 if ((ks
= sensor_find(ksd
, type
, numt
)) == NULL
)
453 return (sysctl_handle_sensor(NULL
, ks
, 0, req
));