xref: /openbsd/sys/kern/kern_sensors.c (revision 3d8817e4)
1 /*	$OpenBSD: kern_sensors.c,v 1.24 2010/04/20 20:49:33 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/proc.h>
22 #include <sys/systm.h>
23 #include <sys/kernel.h>
24 #include <sys/malloc.h>
25 #include <sys/queue.h>
26 #include <sys/types.h>
27 #include <sys/device.h>
28 #include <sys/hotplug.h>
29 #include <sys/timeout.h>
30 #include <sys/workq.h>
31 
32 #include <sys/sensors.h>
33 #include "hotplug.h"
34 
35 int			sensordev_count;
36 SLIST_HEAD(, ksensordev) sensordev_list =
37     SLIST_HEAD_INITIALIZER(sensordev_list);
38 
39 void
40 sensordev_install(struct ksensordev *sensdev)
41 {
42 	struct ksensordev *v, *nv;
43 	int s;
44 
45 	s = splhigh();
46 	if (sensordev_count == 0) {
47 		sensdev->num = 0;
48 		SLIST_INSERT_HEAD(&sensordev_list, sensdev, list);
49 	} else {
50 		for (v = SLIST_FIRST(&sensordev_list);
51 		    (nv = SLIST_NEXT(v, list)) != NULL; v = nv)
52 			if (nv->num - v->num > 1)
53 				break;
54 		sensdev->num = v->num + 1;
55 		SLIST_INSERT_AFTER(v, sensdev, list);
56 	}
57 	sensordev_count++;
58 	splx(s);
59 
60 #if NHOTPLUG > 0
61 	hotplug_device_attach(DV_DULL, "sensordev");
62 #endif
63 }
64 
65 void
66 sensor_attach(struct ksensordev *sensdev, struct ksensor *sens)
67 {
68 	struct ksensor *v, *nv;
69 	struct ksensors_head *sh;
70 	int s, i;
71 
72 	s = splhigh();
73 	sh = &sensdev->sensors_list;
74 	if (sensdev->sensors_count == 0) {
75 		for (i = 0; i < SENSOR_MAX_TYPES; i++)
76 			sensdev->maxnumt[i] = 0;
77 		sens->numt = 0;
78 		SLIST_INSERT_HEAD(sh, sens, list);
79 	} else {
80 		for (v = SLIST_FIRST(sh);
81 		    (nv = SLIST_NEXT(v, list)) != NULL; v = nv)
82 			if (v->type == sens->type && (v->type != nv->type ||
83 			    (v->type == nv->type && nv->numt - v->numt > 1)))
84 				break;
85 		/* sensors of the same type go after each other */
86 		if (v->type == sens->type)
87 			sens->numt = v->numt + 1;
88 		else
89 			sens->numt = 0;
90 		SLIST_INSERT_AFTER(v, sens, list);
91 	}
92 	/* we only increment maxnumt[] if the sensor was added
93 	 * to the last position of sensors of this type
94 	 */
95 	if (sensdev->maxnumt[sens->type] == sens->numt)
96 		sensdev->maxnumt[sens->type]++;
97 	sensdev->sensors_count++;
98 	splx(s);
99 }
100 
101 void
102 sensordev_deinstall(struct ksensordev *sensdev)
103 {
104 	int s;
105 
106 	s = splhigh();
107 	sensordev_count--;
108 	SLIST_REMOVE(&sensordev_list, sensdev, ksensordev, list);
109 	splx(s);
110 
111 #if NHOTPLUG > 0
112 	hotplug_device_detach(DV_DULL, "sensordev");
113 #endif
114 }
115 
116 void
117 sensor_detach(struct ksensordev *sensdev, struct ksensor *sens)
118 {
119 	struct ksensors_head *sh;
120 	int s;
121 
122 	s = splhigh();
123 	sh = &sensdev->sensors_list;
124 	sensdev->sensors_count--;
125 	SLIST_REMOVE(sh, sens, ksensor, list);
126 	/* we only decrement maxnumt[] if this is the tail
127 	 * sensor of this type
128 	 */
129 	if (sens->numt == sensdev->maxnumt[sens->type] - 1)
130 		sensdev->maxnumt[sens->type]--;
131 	splx(s);
132 }
133 
134 int
135 sensordev_get(int num, struct ksensordev **sensdev)
136 {
137 	struct ksensordev *sd;
138 
139 	SLIST_FOREACH(sd, &sensordev_list, list) {
140 		if (sd->num == num) {
141 			*sensdev = sd;
142 			return (0);
143 		}
144 		if (sd->num > num)
145 			return (ENXIO);
146 	}
147 	return (ENOENT);
148 }
149 
150 int
151 sensor_find(int dev, enum sensor_type type, int numt, struct ksensor **ksensorp)
152 {
153 	struct ksensor *s;
154 	struct ksensordev *sensdev;
155 	struct ksensors_head *sh;
156 	int ret;
157 
158 	ret = sensordev_get(dev, &sensdev);
159 	if (ret)
160 		return (ret);
161 
162 	sh = &sensdev->sensors_list;
163 	SLIST_FOREACH(s, sh, list)
164 		if (s->type == type && s->numt == numt) {
165 			*ksensorp = s;
166 			return (0);
167 		}
168 
169 	return (ENOENT);
170 }
171 
172 struct sensor_task {
173 	void				(*func)(void *);
174 	void				*arg;
175 
176 	int				period;
177 	struct timeout			timeout;
178 	volatile enum {
179 		ST_TICKING,
180 		ST_WORKQ,
181 		ST_RUNNING,
182 		ST_DYING,
183 		ST_DEAD
184 	}				state;
185 };
186 
187 void	sensor_task_tick(void *);
188 void	sensor_task_work(void *, void *);
189 
190 struct sensor_task *
191 sensor_task_register(void *arg, void (*func)(void *), int period)
192 {
193 	struct sensor_task *st;
194 
195 	st = malloc(sizeof(struct sensor_task), M_DEVBUF, M_NOWAIT);
196 	if (st == NULL)
197 		return (NULL);
198 
199 	st->func = func;
200 	st->arg = arg;
201 	st->period = period;
202 	timeout_set(&st->timeout, sensor_task_tick, st);
203 
204 	sensor_task_tick(st);
205 
206 	return (st);
207 }
208 
209 void
210 sensor_task_unregister(struct sensor_task *st)
211 {
212 	timeout_del(&st->timeout);
213 
214 	switch (st->state) {
215 	case ST_TICKING:
216 		free(st, M_DEVBUF);
217 		break;
218 
219 	case ST_WORKQ:
220 		st->state = ST_DYING;
221 		break;
222 
223 	case ST_RUNNING:
224 		st->state = ST_DYING;
225 		while (st->state != ST_DEAD)
226 			tsleep(st, 0, "stunr", 0);
227 		free(st, M_DEVBUF);
228 		break;
229 	default:
230 		panic("sensor_task_unregister: unexpected state %d",
231 		    st->state);
232 	}
233 }
234 
235 void
236 sensor_task_tick(void *arg)
237 {
238 	struct sensor_task *st = arg;
239 
240 	/* try to schedule the task */
241 	if (workq_add_task(NULL, 0, sensor_task_work, st, NULL) != 0)
242 		timeout_add_msec(&st->timeout, 500);
243 
244 	st->state = ST_WORKQ;
245 }
246 
247 void
248 sensor_task_work(void *xst, void *arg)
249 {
250 	struct sensor_task *st = xst;
251 
252 	if (st->state == ST_DYING) {
253 		free(st, M_DEVBUF);
254 		return;
255 	}
256 
257 	st->state = ST_RUNNING;
258 	st->func(st->arg);
259 
260 	if (st->state == ST_DYING) {
261 		st->state = ST_DEAD;
262 		wakeup(st);
263 	} else {
264 		st->state = ST_TICKING;
265 		timeout_add_sec(&st->timeout, st->period);
266 	}
267 }
268