1 /* $OpenBSD: kern_sensors.c,v 1.40 2022/12/05 23:18:37 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/malloc.h>
23 #include <sys/queue.h>
24 #include <sys/device.h>
25 #include <sys/hotplug.h>
26 #include <sys/timeout.h>
27 #include <sys/task.h>
28 #include <sys/rwlock.h>
29 #include <sys/atomic.h>
30
31 #include <sys/sensors.h>
32 #include "hotplug.h"
33
34 struct taskq *sensors_taskq;
35 int sensordev_count;
36 SLIST_HEAD(, ksensordev) sensordev_list =
37 SLIST_HEAD_INITIALIZER(sensordev_list);
38
39 void
sensordev_install(struct ksensordev * sensdev)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
sensor_attach(struct ksensordev * sensdev,struct ksensor * sens)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
sensordev_deinstall(struct ksensordev * sensdev)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
sensor_detach(struct ksensordev * sensdev,struct ksensor * sens)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
sensordev_get(int num,struct ksensordev ** sensdev)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
sensor_find(int dev,enum sensor_type type,int numt,struct ksensor ** ksensorp)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 unsigned int period;
177 struct timeout timeout;
178 struct task task;
179 struct rwlock lock;
180 };
181
182 void sensor_task_tick(void *);
183 void sensor_task_work(void *);
184
185 struct sensor_task *
sensor_task_register(void * arg,void (* func)(void *),unsigned int period)186 sensor_task_register(void *arg, void (*func)(void *), unsigned int period)
187 {
188 struct sensor_task *st;
189
190 #ifdef DIAGNOSTIC
191 if (period == 0)
192 panic("sensor_task_register: period is 0");
193 #endif
194
195 if (sensors_taskq == NULL &&
196 (sensors_taskq = taskq_create("sensors", 1, IPL_HIGH, 0)) == NULL)
197 sensors_taskq = systq;
198
199 st = malloc(sizeof(*st), M_DEVBUF, M_NOWAIT);
200 if (st == NULL)
201 return (NULL);
202
203 st->func = func;
204 st->arg = arg;
205 st->period = period;
206 timeout_set(&st->timeout, sensor_task_tick, st);
207 task_set(&st->task, sensor_task_work, st);
208 rw_init(&st->lock, "sensor");
209
210 sensor_task_tick(st);
211
212 return (st);
213 }
214
215 void
sensor_task_unregister(struct sensor_task * st)216 sensor_task_unregister(struct sensor_task *st)
217 {
218 /*
219 * we can't reliably timeout_del or task_del because there's a window
220 * between when they come off the lists and the timeout or task code
221 * actually runs the respective handlers for them. mark the sensor_task
222 * as dying by setting period to 0 and let sensor_task_work mop up.
223 */
224
225 rw_enter_write(&st->lock);
226 st->period = 0;
227 rw_exit_write(&st->lock);
228 }
229
230 void
sensor_task_tick(void * arg)231 sensor_task_tick(void *arg)
232 {
233 struct sensor_task *st = arg;
234 task_add(sensors_taskq, &st->task);
235 }
236
237 static int sensors_quiesced;
238 static int sensors_running;
239
240 void
sensor_quiesce(void)241 sensor_quiesce(void)
242 {
243 sensors_quiesced = 1;
244 while (sensors_running > 0)
245 tsleep_nsec(&sensors_running, PZERO, "sensorpause", INFSLP);
246
247 }
248 void
sensor_restart(void)249 sensor_restart(void)
250 {
251 sensors_quiesced = 0;
252 }
253
254 void
sensor_task_work(void * xst)255 sensor_task_work(void *xst)
256 {
257 struct sensor_task *st = xst;
258 unsigned int period = 0;
259
260 atomic_inc_int(&sensors_running);
261 rw_enter_write(&st->lock);
262 period = st->period;
263 if (period > 0 && !sensors_quiesced)
264 st->func(st->arg);
265 rw_exit_write(&st->lock);
266 if (atomic_dec_int_nv(&sensors_running) == 0) {
267 if (sensors_quiesced)
268 wakeup(&sensors_running);
269 }
270
271 if (period == 0)
272 free(st, M_DEVBUF, sizeof(*st));
273 else
274 timeout_add_sec(&st->timeout, period);
275 }
276