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