Merge branch 'vendor/GCC50'
[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 int              sensordev_idmax;
40 static TAILQ_HEAD(sensordev_list, ksensordev) sensordev_list =
41     TAILQ_HEAD_INITIALIZER(sensordev_list);
42
43 static struct ksensordev *sensordev_get(int);
44 static struct ksensor   *sensor_find(struct ksensordev *, enum sensor_type,
45                             int);
46
47 struct sensor_task {
48         void                            *arg;
49         void                            (*func)(void *);
50
51         int                             period;
52         time_t                          nextrun;        /* time_uptime */
53         int                             running;
54         TAILQ_ENTRY(sensor_task)        entry;
55 };
56
57 static void             sensor_task_thread(void *);
58 static void             sensor_task_schedule(struct sensor_task *);
59
60 static TAILQ_HEAD(, sensor_task) sensor_tasklist =
61     TAILQ_HEAD_INITIALIZER(sensor_tasklist);
62
63 static struct lock      sensor_task_lock =
64     LOCK_INITIALIZER("ksensor_task", 0, LK_CANRECURSE);
65
66 static void             sensordev_sysctl_install(struct ksensordev *);
67 static void             sensordev_sysctl_deinstall(struct ksensordev *);
68 static void             sensor_sysctl_install(struct ksensordev *,
69                             struct ksensor *);
70 static void             sensor_sysctl_deinstall(struct ksensordev *,
71                             struct ksensor *);
72
73 void
74 sensordev_install(struct ksensordev *sensdev)
75 {
76         struct ksensordev *v, *after = NULL;
77         int num = 0;
78
79         SYSCTL_XLOCK();
80
81         TAILQ_FOREACH(v, &sensordev_list, list) {
82                 if (v->num == num) {
83                         ++num;
84                         after = v;
85                 } else if (v->num > num) {
86                         break;
87                 }
88         }
89
90         sensdev->num = num;
91         if (after == NULL) {
92                 KKASSERT(sensdev->num == 0);
93                 TAILQ_INSERT_HEAD(&sensordev_list, sensdev, list);
94         } else {
95                 TAILQ_INSERT_AFTER(&sensordev_list, after, sensdev, list);
96         }
97
98         /* Save max sensor device id */
99         sensordev_idmax = TAILQ_LAST(&sensordev_list, sensordev_list)->num + 1;
100
101         /* Install sysctl node for this sensor device */
102         sensordev_sysctl_install(sensdev);
103
104         SYSCTL_XUNLOCK();
105 }
106
107 void
108 sensor_attach(struct ksensordev *sensdev, struct ksensor *sens)
109 {
110         struct ksensor *v, *nv;
111         struct ksensors_head *sh;
112         int i;
113
114         SYSCTL_XLOCK();
115
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;
120                 sens->numt = 0;
121                 SLIST_INSERT_HEAD(sh, sens, list);
122         } else {
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)))
127                                 break;
128                 /* sensors of the same type go after each other */
129                 if (v->type == sens->type)
130                         sens->numt = v->numt + 1;
131                 else
132                         sens->numt = 0;
133                 SLIST_INSERT_AFTER(v, sens, list);
134         }
135         /*
136          * We only increment maxnumt[] if the sensor was added
137          * to the last position of sensors of this type
138          */
139         if (sensdev->maxnumt[sens->type] == sens->numt)
140                 sensdev->maxnumt[sens->type]++;
141         sensdev->sensors_count++;
142
143         /* Install sysctl node for this sensor */
144         sensor_sysctl_install(sensdev, sens);
145
146         SYSCTL_XUNLOCK();
147 }
148
149 void
150 sensordev_deinstall(struct ksensordev *sensdev)
151 {
152         struct ksensordev *last;
153
154         SYSCTL_XLOCK();
155
156         TAILQ_REMOVE(&sensordev_list, sensdev, list);
157
158         /* Adjust max sensor device id */
159         last = TAILQ_LAST(&sensordev_list, sensordev_list);
160         if (last != NULL)
161                 sensordev_idmax = last->num + 1;
162         else
163                 sensordev_idmax = 0;
164
165         /*
166          * Deinstall sensor device's sysctl node; this also
167          * removes all attached sensors' sysctl nodes.
168          */
169         sensordev_sysctl_deinstall(sensdev);
170
171         SYSCTL_XUNLOCK();
172 }
173
174 void
175 sensor_detach(struct ksensordev *sensdev, struct ksensor *sens)
176 {
177         struct ksensors_head *sh;
178
179         SYSCTL_XLOCK();
180
181         sh = &sensdev->sensors_list;
182         sensdev->sensors_count--;
183         SLIST_REMOVE(sh, sens, ksensor, list);
184         /*
185          * We only decrement maxnumt[] if this is the tail 
186          * sensor of this type
187          */
188         if (sens->numt == sensdev->maxnumt[sens->type] - 1)
189                 sensdev->maxnumt[sens->type]--;
190
191         /* Deinstall sensor's sysctl node */
192         sensor_sysctl_deinstall(sensdev, sens);
193
194         SYSCTL_XUNLOCK();
195 }
196
197 static struct ksensordev *
198 sensordev_get(int num)
199 {
200         struct ksensordev *sd;
201
202         SYSCTL_ASSERT_XLOCKED();
203
204         TAILQ_FOREACH(sd, &sensordev_list, list) {
205                 if (sd->num == num)
206                         return (sd);
207         }
208         return (NULL);
209 }
210
211 static struct ksensor *
212 sensor_find(struct ksensordev *sensdev, enum sensor_type type, int numt)
213 {
214         struct ksensor *s;
215         struct ksensors_head *sh;
216
217         SYSCTL_ASSERT_XLOCKED();
218
219         sh = &sensdev->sensors_list;
220         SLIST_FOREACH(s, sh, list) {
221                 if (s->type == type && s->numt == numt)
222                         return (s);
223         }
224         return (NULL);
225 }
226
227 void
228 sensor_task_register(void *arg, void (*func)(void *), int period)
229 {
230         struct sensor_task      *st;
231
232         st = kmalloc(sizeof(struct sensor_task), M_DEVBUF, M_WAITOK);
233
234         lockmgr(&sensor_task_lock, LK_EXCLUSIVE);
235         st->arg = arg;
236         st->func = func;
237         st->period = period;
238
239         st->running = 1;
240
241         st->nextrun = 0;
242         TAILQ_INSERT_HEAD(&sensor_tasklist, st, entry);
243
244         wakeup(&sensor_tasklist);
245
246         lockmgr(&sensor_task_lock, LK_RELEASE);
247 }
248
249 void
250 sensor_task_unregister(void *arg)
251 {
252         struct sensor_task      *st;
253
254         lockmgr(&sensor_task_lock, LK_EXCLUSIVE);
255         TAILQ_FOREACH(st, &sensor_tasklist, entry)
256                 if (st->arg == arg)
257                         st->running = 0;
258         lockmgr(&sensor_task_lock, LK_RELEASE);
259 }
260
261 static void
262 sensor_task_thread(void *arg)
263 {
264         struct sensor_task      *st, *nst;
265         time_t                  now;
266
267         lockmgr(&sensor_task_lock, LK_EXCLUSIVE);
268
269         for (;;) {
270                 while (TAILQ_EMPTY(&sensor_tasklist)) {
271                         lksleep(&sensor_tasklist, &sensor_task_lock, 0,
272                             "waittask", 0);
273                 }
274
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);
279                 }
280
281                 while ((st = nst) != NULL) {
282                         nst = TAILQ_NEXT(st, entry);
283
284                         if (st->nextrun > now)
285                                 break;
286
287                         /* take it out while we work on it */
288                         TAILQ_REMOVE(&sensor_tasklist, st, entry);
289
290                         if (!st->running) {
291                                 kfree(st, M_DEVBUF);
292                                 continue;
293                         }
294
295                         /* run the task */
296                         st->func(st->arg);
297                         /* stick it back in the tasklist */
298                         sensor_task_schedule(st);
299                 }
300         }
301
302         lockmgr(&sensor_task_lock, LK_RELEASE);
303 }
304
305 static void
306 sensor_task_schedule(struct sensor_task *st)
307 {
308         struct sensor_task      *cst;
309
310         KASSERT(lockstatus(&sensor_task_lock, curthread) == LK_EXCLUSIVE,
311             ("sensor task lock is not held"));
312
313         st->nextrun = time_uptime + st->period;
314
315         TAILQ_FOREACH(cst, &sensor_tasklist, entry) {
316                 if (cst->nextrun > st->nextrun) {
317                         TAILQ_INSERT_BEFORE(cst, st, entry);
318                         return;
319                 }
320         }
321
322         /* must be an empty list, or at the end of the list */
323         TAILQ_INSERT_TAIL(&sensor_tasklist, st, entry);
324 }
325
326 /*
327  * sysctl glue code
328  */
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);
332
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");
337
338 SYSCTL_INT(_hw_sensors, OID_AUTO, dev_idmax, CTLFLAG_RD,
339     &sensordev_idmax, 0, "Max sensor device id");
340
341 static void
342 sensordev_sysctl_install(struct ksensordev *sensdev)
343 {
344         struct sysctl_ctx_list *cl = &sensdev->clist;
345         struct ksensor *s;
346         struct ksensors_head *sh = &sensdev->sensors_list;
347
348         SYSCTL_ASSERT_XLOCKED();
349
350         KASSERT(sensdev->oid == NULL,
351             ("sensor device %s sysctl node already installed", sensdev->xname));
352
353         sysctl_ctx_init(cl);
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",
358                     sensdev->xname);
359                 return;
360         }
361
362         /* Install sysctl nodes for sensors attached to this sensor device */
363         SLIST_FOREACH(s, sh, list)
364                 sensor_sysctl_install(sensdev, s);
365 }
366
367 static void
368 sensor_sysctl_install(struct ksensordev *sensdev, struct ksensor *sens)
369 {
370         char n[32];
371
372         SYSCTL_ASSERT_XLOCKED();
373
374         if (sensdev->oid == NULL) {
375                 /* Sensor device sysctl node is not installed yet */
376                 return;
377         }
378
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));
382
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,
386             "S,sensor", "");
387 }
388
389 static void
390 sensordev_sysctl_deinstall(struct ksensordev *sensdev)
391 {
392         SYSCTL_ASSERT_XLOCKED();
393
394         if (sensdev->oid != NULL) {
395                 sysctl_ctx_free(&sensdev->clist);
396                 sensdev->oid = NULL;
397         }
398 }
399
400 static void
401 sensor_sysctl_deinstall(struct ksensordev *sensdev, struct ksensor *sens)
402 {
403         SYSCTL_ASSERT_XLOCKED();
404
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);
408         }
409         sens->oid = NULL;
410 }
411
412 static int
413 sysctl_handle_sensordev(SYSCTL_HANDLER_ARGS)
414 {
415         struct ksensordev *ksd = arg1;
416         struct sensordev *usd;
417         int error;
418
419         if (req->newptr)
420                 return (EPERM);
421
422         /* Grab a copy, to clear the kernel pointers */
423         usd = kmalloc(sizeof(*usd), M_TEMP, M_WAITOK | M_ZERO);
424         usd->num = ksd->num;
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;
428
429         error = SYSCTL_OUT(req, usd, sizeof(struct sensordev));
430
431         kfree(usd, M_TEMP);
432         return (error);
433 }
434
435 static int
436 sysctl_handle_sensor(SYSCTL_HANDLER_ARGS)
437 {
438         struct ksensor *ks = arg1;
439         struct sensor *us;
440         int error;
441
442         if (req->newptr)
443                 return (EPERM);
444
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));
448         us->tv = ks->tv;
449         us->value = ks->value;
450         us->type = ks->type;
451         us->status = ks->status;
452         us->numt = ks->numt;
453         us->flags = ks->flags;
454
455         error = SYSCTL_OUT(req, us, sizeof(struct sensor));
456
457         kfree(us, M_TEMP);
458         return (error);
459 }
460
461 static int
462 sysctl_sensors_handler(SYSCTL_HANDLER_ARGS)
463 {
464         int *name = arg1;
465         u_int namelen = arg2;
466         struct ksensordev *ksd;
467         struct ksensor *ks;
468         int dev, numt;
469         enum sensor_type type;
470
471         if (namelen != 1 && namelen != 3)
472                 return (ENOTDIR);
473
474         dev = name[0];
475         if ((ksd = sensordev_get(dev)) == NULL)
476                 return (ENOENT);
477
478         if (namelen == 1)
479                 return (sysctl_handle_sensordev(NULL, ksd, 0, req));
480
481         type = name[1];
482         numt = name[2];
483
484         if ((ks = sensor_find(ksd, type, numt)) == NULL)
485                 return (ENOENT);
486         return (sysctl_handle_sensor(NULL, ks, 0, req));
487 }
488
489 static void
490 sensor_sysinit(void *arg __unused)
491 {
492         int error, cpu;
493
494         cpu = ncpus - 1; /* stick to the last CPU */
495         error = kthread_create_cpu(sensor_task_thread, NULL, NULL, cpu,
496             "sensors");
497         if (error)
498                 panic("sensors kthread on cpu%d failed: %d", cpu, error);
499 }
500 SYSINIT(sensor, SI_SUB_PRE_DRIVERS, SI_ORDER_ANY, sensor_sysinit, NULL);