xref: /openbsd-src/sys/kern/kern_sensors.c (revision daf88648c0e349d5c02e1504293082072c981640)
1 /*	$OpenBSD: kern_sensors.c,v 1.16 2006/12/23 17:41:26 deraadt Exp $	*/
2 
3 /*
4  * Copyright (c) 2005 David Gwynne <dlg@openbsd.org>
5  * Copyright (c) 2006 Constantine A. Murenin <cnst+openbsd@bugmail.mojo.ru>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <sys/param.h>
21 #include <sys/systm.h>
22 #include <sys/kernel.h>
23 #include <sys/malloc.h>
24 #include <sys/kthread.h>
25 #include <sys/queue.h>
26 #include <sys/types.h>
27 #include <sys/time.h>
28 #include <sys/device.h>
29 #include <sys/hotplug.h>
30 
31 #include <sys/sensors.h>
32 #include "hotplug.h"
33 
34 int			sensordev_count = 0;
35 SLIST_HEAD(, sensordev)	sensordev_list = SLIST_HEAD_INITIALIZER(sensordev_list);
36 
37 struct sensor_task {
38 	void				*arg;
39 	void				(*func)(void *);
40 
41 	int				period;
42 	time_t				nextrun;
43 	volatile int			running;
44 	TAILQ_ENTRY(sensor_task)	entry;
45 };
46 
47 void	sensor_task_create(void *);
48 void	sensor_task_thread(void *);
49 void	sensor_task_schedule(struct sensor_task *);
50 
51 TAILQ_HEAD(, sensor_task) tasklist = TAILQ_HEAD_INITIALIZER(tasklist);
52 
53 void
54 sensordev_install(struct sensordev *sensdev)
55 {
56 	struct sensordev *v, *nv;
57 	int s;
58 
59 	s = splhigh();
60 	if (sensordev_count == 0) {
61 		sensdev->num = 0;
62 		SLIST_INSERT_HEAD(&sensordev_list, sensdev, list);
63 	} else {
64 		for (v = SLIST_FIRST(&sensordev_list);
65 		    (nv = SLIST_NEXT(v, list)) != NULL; v = nv)
66 			if (nv->num - v->num > 1)
67 				break;
68 		sensdev->num = v->num + 1;
69 		SLIST_INSERT_AFTER(v, sensdev, list);
70 	}
71 	sensordev_count++;
72 	splx(s);
73 
74 #if NHOTPLUG > 0
75 	hotplug_device_attach(DV_DULL, "sensordev");
76 #endif
77 }
78 
79 void
80 sensor_attach(struct sensordev *sensdev, struct sensor *sens)
81 {
82 	struct sensor *v, *nv;
83 	struct sensors_head *sh;
84 	int s, i;
85 
86 	s = splhigh();
87 	sh = &sensdev->sensors_list;
88 	if (sensdev->sensors_count == 0) {
89 		for (i = 0; i < SENSOR_MAX_TYPES; i++)
90 			sensdev->maxnumt[i] = 0;
91 		sens->numt = 0;
92 		SLIST_INSERT_HEAD(sh, sens, list);
93 	} else {
94 		for (v = SLIST_FIRST(sh);
95 		    (nv = SLIST_NEXT(v, list)) != NULL; v = nv)
96 			if (v->type == sens->type && (v->type != nv->type ||
97 			    (v->type == nv->type && nv->numt - v->numt > 1)))
98 				break;
99 		/* sensors of the same type go after each other */
100 		if (v->type == sens->type)
101 			sens->numt = v->numt + 1;
102 		else
103 			sens->numt = 0;
104 		SLIST_INSERT_AFTER(v, sens, list);
105 	}
106 	/* we only increment maxnumt[] if the sensor was added
107 	 * to the last position of sensors of this type
108 	 */
109 	if (sensdev->maxnumt[sens->type] == sens->numt)
110 		sensdev->maxnumt[sens->type]++;
111 	sensdev->sensors_count++;
112 	splx(s);
113 }
114 
115 void
116 sensordev_deinstall(struct sensordev *sensdev)
117 {
118 	int s;
119 
120 	s = splhigh();
121 	sensordev_count--;
122 	SLIST_REMOVE(&sensordev_list, sensdev, sensordev, list);
123 	splx(s);
124 
125 #if NHOTPLUG > 0
126 	hotplug_device_detach(DV_DULL, "sensordev");
127 #endif
128 }
129 
130 void
131 sensor_detach(struct sensordev *sensdev, struct sensor *sens)
132 {
133 	struct sensors_head *sh;
134 	int s;
135 
136 	s = splhigh();
137 	sh = &sensdev->sensors_list;
138 	sensdev->sensors_count--;
139 	SLIST_REMOVE(sh, sens, sensor, list);
140 	/* we only decrement maxnumt[] if this is the tail
141 	 * sensor of this type
142 	 */
143 	if (sens->numt == sensdev->maxnumt[sens->type] - 1)
144 		sensdev->maxnumt[sens->type]--;
145 	splx(s);
146 }
147 
148 struct sensordev *
149 sensordev_get(int num)
150 {
151 	struct sensordev *sd;
152 
153 	SLIST_FOREACH(sd, &sensordev_list, list)
154 		if (sd->num == num)
155 			return (sd);
156 
157 	return (NULL);
158 }
159 
160 struct sensor *
161 sensor_find(int dev, enum sensor_type type, int numt)
162 {
163 	struct sensor *s;
164 	struct sensordev *sensdev;
165 	struct sensors_head *sh;
166 
167 	sensdev = sensordev_get(dev);
168 	if (sensdev == NULL)
169 		return (NULL);
170 
171 	sh = &sensdev->sensors_list;
172 	SLIST_FOREACH(s, sh, list)
173 		if (s->type == type && s->numt == numt)
174 			return (s);
175 
176 	return (NULL);
177 }
178 
179 int
180 sensor_task_register(void *arg, void (*func)(void *), int period)
181 {
182 	struct sensor_task	*st;
183 
184 	st = malloc(sizeof(struct sensor_task), M_DEVBUF, M_NOWAIT);
185 	if (st == NULL)
186 		return (1);
187 
188 	st->arg = arg;
189 	st->func = func;
190 	st->period = period;
191 
192 	st->running = 1;
193 
194 	if (TAILQ_EMPTY(&tasklist))
195 		kthread_create_deferred(sensor_task_create, NULL);
196 
197 	st->nextrun = 0;
198 	TAILQ_INSERT_HEAD(&tasklist, st, entry);
199 	wakeup(&tasklist);
200 
201 	return (0);
202 }
203 
204 void
205 sensor_task_unregister(void *arg)
206 {
207 	struct sensor_task	*st;
208 
209 	TAILQ_FOREACH(st, &tasklist, entry) {
210 		if (st->arg == arg)
211 			st->running = 0;
212 	}
213 }
214 
215 void
216 sensor_task_create(void *arg)
217 {
218 	if (kthread_create(sensor_task_thread, NULL, NULL, "sensors") != 0)
219 		panic("sensors kthread");
220 }
221 
222 void
223 sensor_task_thread(void *arg)
224 {
225 	struct sensor_task	*st, *nst;
226 	time_t			now;
227 
228 	while (!TAILQ_EMPTY(&tasklist)) {
229 		while ((nst = TAILQ_FIRST(&tasklist))->nextrun >
230 		    (now = time_uptime))
231 			tsleep(&tasklist, PWAIT, "timeout",
232 			    (nst->nextrun - now) * hz);
233 
234 		while ((st = nst) != NULL) {
235 			nst = TAILQ_NEXT(st, entry);
236 
237 			if (st->nextrun > now)
238 				break;
239 
240 			/* take it out while we work on it */
241 			TAILQ_REMOVE(&tasklist, st, entry);
242 
243 			if (!st->running) {
244 				free(st, M_DEVBUF);
245 				continue;
246 			}
247 
248 			/* run the task */
249 			st->func(st->arg);
250 			/* stick it back in the tasklist */
251 			sensor_task_schedule(st);
252 		}
253 	}
254 
255 	kthread_exit(0);
256 }
257 
258 void
259 sensor_task_schedule(struct sensor_task *st)
260 {
261 	struct sensor_task 	*cst;
262 
263 	st->nextrun = time_uptime + st->period;
264 
265 	TAILQ_FOREACH(cst, &tasklist, entry) {
266 		if (cst->nextrun > st->nextrun) {
267 			TAILQ_INSERT_BEFORE(cst, st, entry);
268 			return;
269 		}
270 	}
271 
272 	/* must be an empty list, or at the end of the list */
273 	TAILQ_INSERT_TAIL(&tasklist, st, entry);
274 }
275 
276