xref: /openbsd-src/sys/kern/kern_sensors.c (revision 4c4510f0af30accadb89e004ee38d96ebe12a3c8)
1 /*	$OpenBSD: kern_sensors.c,v 1.18 2007/06/01 04:15:45 dlg 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 #include <sys/workq.h>
31 
32 #include <sys/sensors.h>
33 #include "hotplug.h"
34 
35 int			sensordev_count = 0;
36 SLIST_HEAD(, ksensordev) sensordev_list = SLIST_HEAD_INITIALIZER(sensordev_list);
37 
38 struct sensor_task {
39 	void				(*func)(void *);
40 	void				*arg;
41 
42 	int				period;
43 	time_t				nextrun;
44 	TAILQ_ENTRY(sensor_task)	entry;
45 };
46 
47 struct timeout sensor_task_to;
48 TAILQ_HEAD(, sensor_task) sensor_task_list =
49     TAILQ_HEAD_INITIALIZER(sensor_task_list);
50 
51 void	sensor_task_tick(void *);
52 void	sensor_task_schedule(struct sensor_task *);
53 void	sensor_task_work(void *, void *);
54 
55 void
56 sensordev_install(struct ksensordev *sensdev)
57 {
58 	struct ksensordev *v, *nv;
59 	int s;
60 
61 	s = splhigh();
62 	if (sensordev_count == 0) {
63 		sensdev->num = 0;
64 		SLIST_INSERT_HEAD(&sensordev_list, sensdev, list);
65 	} else {
66 		for (v = SLIST_FIRST(&sensordev_list);
67 		    (nv = SLIST_NEXT(v, list)) != NULL; v = nv)
68 			if (nv->num - v->num > 1)
69 				break;
70 		sensdev->num = v->num + 1;
71 		SLIST_INSERT_AFTER(v, sensdev, list);
72 	}
73 	sensordev_count++;
74 	splx(s);
75 
76 #if NHOTPLUG > 0
77 	hotplug_device_attach(DV_DULL, "sensordev");
78 #endif
79 }
80 
81 void
82 sensor_attach(struct ksensordev *sensdev, struct ksensor *sens)
83 {
84 	struct ksensor *v, *nv;
85 	struct ksensors_head *sh;
86 	int s, i;
87 
88 	s = splhigh();
89 	sh = &sensdev->sensors_list;
90 	if (sensdev->sensors_count == 0) {
91 		for (i = 0; i < SENSOR_MAX_TYPES; i++)
92 			sensdev->maxnumt[i] = 0;
93 		sens->numt = 0;
94 		SLIST_INSERT_HEAD(sh, sens, list);
95 	} else {
96 		for (v = SLIST_FIRST(sh);
97 		    (nv = SLIST_NEXT(v, list)) != NULL; v = nv)
98 			if (v->type == sens->type && (v->type != nv->type ||
99 			    (v->type == nv->type && nv->numt - v->numt > 1)))
100 				break;
101 		/* sensors of the same type go after each other */
102 		if (v->type == sens->type)
103 			sens->numt = v->numt + 1;
104 		else
105 			sens->numt = 0;
106 		SLIST_INSERT_AFTER(v, sens, list);
107 	}
108 	/* we only increment maxnumt[] if the sensor was added
109 	 * to the last position of sensors of this type
110 	 */
111 	if (sensdev->maxnumt[sens->type] == sens->numt)
112 		sensdev->maxnumt[sens->type]++;
113 	sensdev->sensors_count++;
114 	splx(s);
115 }
116 
117 void
118 sensordev_deinstall(struct ksensordev *sensdev)
119 {
120 	int s;
121 
122 	s = splhigh();
123 	sensordev_count--;
124 	SLIST_REMOVE(&sensordev_list, sensdev, ksensordev, list);
125 	splx(s);
126 
127 #if NHOTPLUG > 0
128 	hotplug_device_detach(DV_DULL, "sensordev");
129 #endif
130 }
131 
132 void
133 sensor_detach(struct ksensordev *sensdev, struct ksensor *sens)
134 {
135 	struct ksensors_head *sh;
136 	int s;
137 
138 	s = splhigh();
139 	sh = &sensdev->sensors_list;
140 	sensdev->sensors_count--;
141 	SLIST_REMOVE(sh, sens, ksensor, list);
142 	/* we only decrement maxnumt[] if this is the tail
143 	 * sensor of this type
144 	 */
145 	if (sens->numt == sensdev->maxnumt[sens->type] - 1)
146 		sensdev->maxnumt[sens->type]--;
147 	splx(s);
148 }
149 
150 struct ksensordev *
151 sensordev_get(int num)
152 {
153 	struct ksensordev *sd;
154 
155 	SLIST_FOREACH(sd, &sensordev_list, list)
156 		if (sd->num == num)
157 			return (sd);
158 
159 	return (NULL);
160 }
161 
162 struct ksensor *
163 sensor_find(int dev, enum sensor_type type, int numt)
164 {
165 	struct ksensor *s;
166 	struct ksensordev *sensdev;
167 	struct ksensors_head *sh;
168 
169 	sensdev = sensordev_get(dev);
170 	if (sensdev == NULL)
171 		return (NULL);
172 
173 	sh = &sensdev->sensors_list;
174 	SLIST_FOREACH(s, sh, list)
175 		if (s->type == type && s->numt == numt)
176 			return (s);
177 
178 	return (NULL);
179 }
180 
181 int
182 sensor_task_register(void *arg, void (*func)(void *), int period)
183 {
184 	struct sensor_task	*st;
185 
186 	st = malloc(sizeof(struct sensor_task), M_DEVBUF, M_NOWAIT);
187 	if (st == NULL)
188 		return (1);
189 
190 	st->arg = arg;
191 	st->func = func;
192 	st->period = period;
193 	st->nextrun = 0;
194 
195 	if (TAILQ_EMPTY(&sensor_task_list))
196 		timeout_set(&sensor_task_to, sensor_task_tick, NULL);
197 	else
198 		timeout_del(&sensor_task_to);
199 
200 	TAILQ_INSERT_HEAD(&sensor_task_list, st, entry);
201 
202 	sensor_task_tick(NULL);
203 
204 	return (0);
205 }
206 
207 void
208 sensor_task_unregister(void *arg)
209 {
210 	struct sensor_task	*st, *nst;
211 
212 	timeout_del(&sensor_task_to);
213 
214 	nst = TAILQ_FIRST(&sensor_task_list);
215 	while (nst != NULL) {
216 		st = nst;
217 		nst = TAILQ_NEXT(st, entry);
218 
219 		if (st->arg == arg)
220 			free(st, M_DEVBUF);
221 	}
222 
223 	if (TAILQ_EMPTY(&sensor_task_list))
224 		return;
225 
226 	sensor_task_tick(NULL);
227 }
228 
229 void
230 sensor_task_tick(void *arg)
231 {
232 	struct sensor_task	*st, *nst;
233 	time_t			now = time_uptime;
234 
235 #ifdef DIAGNOSTIC
236 	if (TAILQ_EMPTY(&sensor_task_list))
237 		panic("sensor task tick for no sensors");
238 #endif
239 
240 	nst = TAILQ_FIRST(&sensor_task_list);
241 	while (nst->nextrun <= now) {
242 		st = nst;
243 		nst = TAILQ_NEXT(st, entry);
244 
245 		/* try to schedule the task */
246 		if (workq_add_task(NULL, 0, sensor_task_work, st, NULL) != 0) {
247 			timeout_add(&sensor_task_to, hz);
248 			return;
249 		}
250 
251 		/* take it out while we work on it */
252 		TAILQ_REMOVE(&sensor_task_list, st, entry);
253 
254 		if (nst == NULL)
255 			return;
256 	}
257 
258 	timeout_add(&sensor_task_to, (nst->nextrun - now) * hz);
259 }
260 
261 void
262 sensor_task_work(void *xst, void *arg)
263 {
264 	struct sensor_task	*st = xst;
265 
266 	timeout_del(&sensor_task_to);
267 
268 	st->func(st->arg);
269 	sensor_task_schedule(st);
270 
271 	sensor_task_tick(NULL);
272 }
273 
274 void
275 sensor_task_schedule(struct sensor_task *st)
276 {
277 	struct sensor_task	*cst;
278 
279 	st->nextrun = time_uptime + st->period;
280 
281 	TAILQ_FOREACH(cst, &sensor_task_list, entry) {
282 		if (cst->nextrun > st->nextrun) {
283 			TAILQ_INSERT_BEFORE(cst, st, entry);
284 			return;
285 		}
286 	}
287 
288 	/* must be an empty list, or at the end of the list */
289 	TAILQ_INSERT_TAIL(&sensor_task_list, st, entry);
290 }
291