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