xref: /openbsd-src/sys/kern/kern_sensors.c (revision 724e05679fdfea6472e74729cd33a9f81142fc85)
1 /*	$OpenBSD: kern_sensors.c,v 1.27 2013/12/09 17:39:08 mikeb 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/task.h>
30 #include <sys/rwlock.h>
31 
32 #include <sys/sensors.h>
33 #include "hotplug.h"
34 
35 struct taskq		*sensors_taskq;
36 int			sensordev_count;
37 SLIST_HEAD(, ksensordev) sensordev_list =
38     SLIST_HEAD_INITIALIZER(sensordev_list);
39 
40 void
41 sensordev_install(struct ksensordev *sensdev)
42 {
43 	struct ksensordev *v, *nv;
44 	int s;
45 
46 	s = splhigh();
47 	if (sensordev_count == 0) {
48 		sensdev->num = 0;
49 		SLIST_INSERT_HEAD(&sensordev_list, sensdev, list);
50 	} else {
51 		for (v = SLIST_FIRST(&sensordev_list);
52 		    (nv = SLIST_NEXT(v, list)) != NULL; v = nv)
53 			if (nv->num - v->num > 1)
54 				break;
55 		sensdev->num = v->num + 1;
56 		SLIST_INSERT_AFTER(v, sensdev, list);
57 	}
58 	sensordev_count++;
59 	splx(s);
60 
61 #if NHOTPLUG > 0
62 	hotplug_device_attach(DV_DULL, "sensordev");
63 #endif
64 }
65 
66 void
67 sensor_attach(struct ksensordev *sensdev, struct ksensor *sens)
68 {
69 	struct ksensor *v, *nv;
70 	struct ksensors_head *sh;
71 	int s, i;
72 
73 	s = splhigh();
74 	sh = &sensdev->sensors_list;
75 	if (sensdev->sensors_count == 0) {
76 		for (i = 0; i < SENSOR_MAX_TYPES; i++)
77 			sensdev->maxnumt[i] = 0;
78 		sens->numt = 0;
79 		SLIST_INSERT_HEAD(sh, sens, list);
80 	} else {
81 		for (v = SLIST_FIRST(sh);
82 		    (nv = SLIST_NEXT(v, list)) != NULL; v = nv)
83 			if (v->type == sens->type && (v->type != nv->type ||
84 			    (v->type == nv->type && nv->numt - v->numt > 1)))
85 				break;
86 		/* sensors of the same type go after each other */
87 		if (v->type == sens->type)
88 			sens->numt = v->numt + 1;
89 		else
90 			sens->numt = 0;
91 		SLIST_INSERT_AFTER(v, sens, list);
92 	}
93 	/* we only increment maxnumt[] if the sensor was added
94 	 * to the last position of sensors of this type
95 	 */
96 	if (sensdev->maxnumt[sens->type] == sens->numt)
97 		sensdev->maxnumt[sens->type]++;
98 	sensdev->sensors_count++;
99 	splx(s);
100 }
101 
102 void
103 sensordev_deinstall(struct ksensordev *sensdev)
104 {
105 	int s;
106 
107 	s = splhigh();
108 	sensordev_count--;
109 	SLIST_REMOVE(&sensordev_list, sensdev, ksensordev, list);
110 	splx(s);
111 
112 #if NHOTPLUG > 0
113 	hotplug_device_detach(DV_DULL, "sensordev");
114 #endif
115 }
116 
117 void
118 sensor_detach(struct ksensordev *sensdev, struct ksensor *sens)
119 {
120 	struct ksensors_head *sh;
121 	int s;
122 
123 	s = splhigh();
124 	sh = &sensdev->sensors_list;
125 	sensdev->sensors_count--;
126 	SLIST_REMOVE(sh, sens, ksensor, list);
127 	/* we only decrement maxnumt[] if this is the tail
128 	 * sensor of this type
129 	 */
130 	if (sens->numt == sensdev->maxnumt[sens->type] - 1)
131 		sensdev->maxnumt[sens->type]--;
132 	splx(s);
133 }
134 
135 int
136 sensordev_get(int num, struct ksensordev **sensdev)
137 {
138 	struct ksensordev *sd;
139 
140 	SLIST_FOREACH(sd, &sensordev_list, list) {
141 		if (sd->num == num) {
142 			*sensdev = sd;
143 			return (0);
144 		}
145 		if (sd->num > num)
146 			return (ENXIO);
147 	}
148 	return (ENOENT);
149 }
150 
151 int
152 sensor_find(int dev, enum sensor_type type, int numt, struct ksensor **ksensorp)
153 {
154 	struct ksensor *s;
155 	struct ksensordev *sensdev;
156 	struct ksensors_head *sh;
157 	int ret;
158 
159 	ret = sensordev_get(dev, &sensdev);
160 	if (ret)
161 		return (ret);
162 
163 	sh = &sensdev->sensors_list;
164 	SLIST_FOREACH(s, sh, list)
165 		if (s->type == type && s->numt == numt) {
166 			*ksensorp = s;
167 			return (0);
168 		}
169 
170 	return (ENOENT);
171 }
172 
173 struct sensor_task {
174 	void				(*func)(void *);
175 	void				*arg;
176 
177 	unsigned int			period;
178 	struct timeout			timeout;
179 	struct task			task;
180 	struct rwlock			lock;
181 };
182 
183 void	sensor_task_tick(void *);
184 void	sensor_task_work(void *, void *);
185 
186 struct sensor_task *
187 sensor_task_register(void *arg, void (*func)(void *), unsigned int period)
188 {
189 	struct sensor_task *st;
190 
191 #ifdef DIAGNOSTIC
192 	if (period == 0)
193 		panic("sensor_task_register: period is 0");
194 #endif
195 
196 	if (sensors_taskq == NULL &&
197 	    (sensors_taskq = taskq_create("sensors", 1, IPL_HIGH)) == NULL)
198 		sensors_taskq = systq;
199 
200 	st = malloc(sizeof(struct sensor_task), M_DEVBUF, M_NOWAIT);
201 	if (st == NULL)
202 		return (NULL);
203 
204 	st->func = func;
205 	st->arg = arg;
206 	st->period = period;
207 	timeout_set(&st->timeout, sensor_task_tick, st);
208 	task_set(&st->task, sensor_task_work, st, NULL);
209 	rw_init(&st->lock, "sensor");
210 
211 	sensor_task_tick(st);
212 
213 	return (st);
214 }
215 
216 void
217 sensor_task_unregister(struct sensor_task *st)
218 {
219 	/*
220 	 * we can't reliably timeout_del or task_del because there's a window
221 	 * between when they come off the lists and the timeout or task code
222 	 * actually runs the respective handlers for them. mark the sensor_task
223 	 * as dying by setting period to 0 and let sensor_task_work mop up.
224 	 */
225 
226 	rw_enter_write(&st->lock);
227 	st->period = 0;
228 	rw_exit_write(&st->lock);
229 }
230 
231 void
232 sensor_task_tick(void *arg)
233 {
234 	struct sensor_task *st = arg;
235 	task_add(sensors_taskq, &st->task);
236 }
237 
238 void
239 sensor_task_work(void *xst, void *arg)
240 {
241 	struct sensor_task *st = xst;
242 	unsigned int period = 0;
243 
244 	rw_enter_write(&st->lock);
245 	period = st->period;
246 	if (period > 0)
247 		st->func(st->arg);
248 	rw_exit_write(&st->lock);
249 
250 	if (period == 0)
251 		free(st, M_DEVBUF);
252 	else
253 		timeout_add_sec(&st->timeout, period);
254 }
255