1*2b46a8cbSderaadt /* $OpenBSD: kern_sensors.c,v 1.40 2022/12/05 23:18:37 deraadt Exp $ */
28a37ea4cSdlg
38a37ea4cSdlg /*
48a37ea4cSdlg * Copyright (c) 2005 David Gwynne <dlg@openbsd.org>
555e0e94aSderaadt * Copyright (c) 2006 Constantine A. Murenin <cnst+openbsd@bugmail.mojo.ru>
68a37ea4cSdlg *
78a37ea4cSdlg * Permission to use, copy, modify, and distribute this software for any
88a37ea4cSdlg * purpose with or without fee is hereby granted, provided that the above
98a37ea4cSdlg * copyright notice and this permission notice appear in all copies.
108a37ea4cSdlg *
118a37ea4cSdlg * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
128a37ea4cSdlg * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
138a37ea4cSdlg * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
148a37ea4cSdlg * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
158a37ea4cSdlg * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
168a37ea4cSdlg * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
178a37ea4cSdlg * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
188a37ea4cSdlg */
198a37ea4cSdlg
208a37ea4cSdlg #include <sys/param.h>
21b6582696Sdlg #include <sys/systm.h>
22b6582696Sdlg #include <sys/malloc.h>
238a37ea4cSdlg #include <sys/queue.h>
243a6813f9Sjason #include <sys/device.h>
2504652f9aShenning #include <sys/hotplug.h>
26abd9fc28Sdlg #include <sys/timeout.h>
27b3316ca5Sdlg #include <sys/task.h>
28b3316ca5Sdlg #include <sys/rwlock.h>
291b45cbc2Sderaadt #include <sys/atomic.h>
308a37ea4cSdlg
318a37ea4cSdlg #include <sys/sensors.h>
32bb73aa1fSmk #include "hotplug.h"
338a37ea4cSdlg
34724e0567Smikeb struct taskq *sensors_taskq;
35f7811f45Sderaadt int sensordev_count;
36abd9fc28Sdlg SLIST_HEAD(, ksensordev) sensordev_list =
37abd9fc28Sdlg SLIST_HEAD_INITIALIZER(sensordev_list);
38b6582696Sdlg
3992bc2975Sgrange void
sensordev_install(struct ksensordev * sensdev)40275cbf62Sderaadt sensordev_install(struct ksensordev *sensdev)
4192bc2975Sgrange {
42275cbf62Sderaadt struct ksensordev *v, *nv;
4392bc2975Sgrange int s;
4492bc2975Sgrange
4592bc2975Sgrange s = splhigh();
4655e0e94aSderaadt if (sensordev_count == 0) {
4755e0e94aSderaadt sensdev->num = 0;
4855e0e94aSderaadt SLIST_INSERT_HEAD(&sensordev_list, sensdev, list);
4992bc2975Sgrange } else {
5055e0e94aSderaadt for (v = SLIST_FIRST(&sensordev_list);
5192bc2975Sgrange (nv = SLIST_NEXT(v, list)) != NULL; v = nv)
5292bc2975Sgrange if (nv->num - v->num > 1)
53bd2c6f01Sjasper break;
5455e0e94aSderaadt sensdev->num = v->num + 1;
5555e0e94aSderaadt SLIST_INSERT_AFTER(v, sensdev, list);
5692bc2975Sgrange }
5755e0e94aSderaadt sensordev_count++;
5892bc2975Sgrange splx(s);
59bb73aa1fSmk
60bb73aa1fSmk #if NHOTPLUG > 0
6155e0e94aSderaadt hotplug_device_attach(DV_DULL, "sensordev");
62bb73aa1fSmk #endif
6392bc2975Sgrange }
6492bc2975Sgrange
6592bc2975Sgrange void
sensor_attach(struct ksensordev * sensdev,struct ksensor * sens)66275cbf62Sderaadt sensor_attach(struct ksensordev *sensdev, struct ksensor *sens)
6755e0e94aSderaadt {
68275cbf62Sderaadt struct ksensor *v, *nv;
69275cbf62Sderaadt struct ksensors_head *sh;
7055e0e94aSderaadt int s, i;
7155e0e94aSderaadt
7255e0e94aSderaadt s = splhigh();
7355e0e94aSderaadt sh = &sensdev->sensors_list;
7455e0e94aSderaadt if (sensdev->sensors_count == 0) {
7555e0e94aSderaadt for (i = 0; i < SENSOR_MAX_TYPES; i++)
7655e0e94aSderaadt sensdev->maxnumt[i] = 0;
7755e0e94aSderaadt sens->numt = 0;
7855e0e94aSderaadt SLIST_INSERT_HEAD(sh, sens, list);
7955e0e94aSderaadt } else {
8055e0e94aSderaadt for (v = SLIST_FIRST(sh);
8155e0e94aSderaadt (nv = SLIST_NEXT(v, list)) != NULL; v = nv)
8255e0e94aSderaadt if (v->type == sens->type && (v->type != nv->type ||
8355e0e94aSderaadt (v->type == nv->type && nv->numt - v->numt > 1)))
8455e0e94aSderaadt break;
8555e0e94aSderaadt /* sensors of the same type go after each other */
8655e0e94aSderaadt if (v->type == sens->type)
8755e0e94aSderaadt sens->numt = v->numt + 1;
8855e0e94aSderaadt else
8955e0e94aSderaadt sens->numt = 0;
9055e0e94aSderaadt SLIST_INSERT_AFTER(v, sens, list);
9155e0e94aSderaadt }
9255e0e94aSderaadt /* we only increment maxnumt[] if the sensor was added
9355e0e94aSderaadt * to the last position of sensors of this type
9455e0e94aSderaadt */
9555e0e94aSderaadt if (sensdev->maxnumt[sens->type] == sens->numt)
9655e0e94aSderaadt sensdev->maxnumt[sens->type]++;
9755e0e94aSderaadt sensdev->sensors_count++;
9855e0e94aSderaadt splx(s);
9955e0e94aSderaadt }
10055e0e94aSderaadt
10155e0e94aSderaadt void
sensordev_deinstall(struct ksensordev * sensdev)102275cbf62Sderaadt sensordev_deinstall(struct ksensordev *sensdev)
10392bc2975Sgrange {
10492bc2975Sgrange int s;
10592bc2975Sgrange
10692bc2975Sgrange s = splhigh();
10755e0e94aSderaadt sensordev_count--;
108275cbf62Sderaadt SLIST_REMOVE(&sensordev_list, sensdev, ksensordev, list);
10992bc2975Sgrange splx(s);
110bb73aa1fSmk
111bb73aa1fSmk #if NHOTPLUG > 0
11255e0e94aSderaadt hotplug_device_detach(DV_DULL, "sensordev");
113bb73aa1fSmk #endif
11492bc2975Sgrange }
11592bc2975Sgrange
11655e0e94aSderaadt void
sensor_detach(struct ksensordev * sensdev,struct ksensor * sens)117275cbf62Sderaadt sensor_detach(struct ksensordev *sensdev, struct ksensor *sens)
11855e0e94aSderaadt {
119275cbf62Sderaadt struct ksensors_head *sh;
12055e0e94aSderaadt int s;
12155e0e94aSderaadt
12255e0e94aSderaadt s = splhigh();
12355e0e94aSderaadt sh = &sensdev->sensors_list;
12455e0e94aSderaadt sensdev->sensors_count--;
125275cbf62Sderaadt SLIST_REMOVE(sh, sens, ksensor, list);
12655e0e94aSderaadt /* we only decrement maxnumt[] if this is the tail
12755e0e94aSderaadt * sensor of this type
12855e0e94aSderaadt */
12955e0e94aSderaadt if (sens->numt == sensdev->maxnumt[sens->type] - 1)
13055e0e94aSderaadt sensdev->maxnumt[sens->type]--;
13155e0e94aSderaadt splx(s);
13255e0e94aSderaadt }
13355e0e94aSderaadt
134f7811f45Sderaadt int
sensordev_get(int num,struct ksensordev ** sensdev)135f7811f45Sderaadt sensordev_get(int num, struct ksensordev **sensdev)
13655e0e94aSderaadt {
137275cbf62Sderaadt struct ksensordev *sd;
13855e0e94aSderaadt
139f7811f45Sderaadt SLIST_FOREACH(sd, &sensordev_list, list) {
140f7811f45Sderaadt if (sd->num == num) {
141f7811f45Sderaadt *sensdev = sd;
142f7811f45Sderaadt return (0);
143f7811f45Sderaadt }
144f7811f45Sderaadt if (sd->num > num)
145f7811f45Sderaadt return (ENXIO);
146f7811f45Sderaadt }
147f7811f45Sderaadt return (ENOENT);
14855e0e94aSderaadt }
14955e0e94aSderaadt
150f7811f45Sderaadt int
sensor_find(int dev,enum sensor_type type,int numt,struct ksensor ** ksensorp)151f7811f45Sderaadt sensor_find(int dev, enum sensor_type type, int numt, struct ksensor **ksensorp)
1526c848b7fSdlg {
153275cbf62Sderaadt struct ksensor *s;
154275cbf62Sderaadt struct ksensordev *sensdev;
155275cbf62Sderaadt struct ksensors_head *sh;
156f7811f45Sderaadt int ret;
1576c848b7fSdlg
158f7811f45Sderaadt ret = sensordev_get(dev, &sensdev);
159f7811f45Sderaadt if (ret)
160f7811f45Sderaadt return (ret);
16155e0e94aSderaadt
16255e0e94aSderaadt sh = &sensdev->sensors_list;
16355e0e94aSderaadt SLIST_FOREACH(s, sh, list)
164f7811f45Sderaadt if (s->type == type && s->numt == numt) {
165f7811f45Sderaadt *ksensorp = s;
166f7811f45Sderaadt return (0);
167f7811f45Sderaadt }
1686c848b7fSdlg
169f7811f45Sderaadt return (ENOENT);
1706c848b7fSdlg }
1716c848b7fSdlg
172abd9fc28Sdlg struct sensor_task {
173abd9fc28Sdlg void (*func)(void *);
174abd9fc28Sdlg void *arg;
175abd9fc28Sdlg
176b3316ca5Sdlg unsigned int period;
177abd9fc28Sdlg struct timeout timeout;
178b3316ca5Sdlg struct task task;
179b3316ca5Sdlg struct rwlock lock;
180abd9fc28Sdlg };
181abd9fc28Sdlg
182abd9fc28Sdlg void sensor_task_tick(void *);
183e4195480Sdlg void sensor_task_work(void *);
184abd9fc28Sdlg
185abd9fc28Sdlg struct sensor_task *
sensor_task_register(void * arg,void (* func)(void *),unsigned int period)186b3316ca5Sdlg sensor_task_register(void *arg, void (*func)(void *), unsigned int period)
187b6582696Sdlg {
188b6582696Sdlg struct sensor_task *st;
189b6582696Sdlg
190b3316ca5Sdlg #ifdef DIAGNOSTIC
191b3316ca5Sdlg if (period == 0)
192b3316ca5Sdlg panic("sensor_task_register: period is 0");
193b3316ca5Sdlg #endif
194b3316ca5Sdlg
195724e0567Smikeb if (sensors_taskq == NULL &&
19679ea9c08Sdlg (sensors_taskq = taskq_create("sensors", 1, IPL_HIGH, 0)) == NULL)
197724e0567Smikeb sensors_taskq = systq;
198724e0567Smikeb
199a11de6bdStedu st = malloc(sizeof(*st), M_DEVBUF, M_NOWAIT);
200b6582696Sdlg if (st == NULL)
201abd9fc28Sdlg return (NULL);
202b6582696Sdlg
203b6582696Sdlg st->func = func;
204abd9fc28Sdlg st->arg = arg;
205b6582696Sdlg st->period = period;
206abd9fc28Sdlg timeout_set(&st->timeout, sensor_task_tick, st);
207e4195480Sdlg task_set(&st->task, sensor_task_work, st);
208b3316ca5Sdlg rw_init(&st->lock, "sensor");
209ad43598bSderaadt
210abd9fc28Sdlg sensor_task_tick(st);
211ad43598bSderaadt
212abd9fc28Sdlg return (st);
213b6582696Sdlg }
214b6582696Sdlg
215b6582696Sdlg void
sensor_task_unregister(struct sensor_task * st)216abd9fc28Sdlg sensor_task_unregister(struct sensor_task *st)
217b6582696Sdlg {
218b3316ca5Sdlg /*
219b3316ca5Sdlg * we can't reliably timeout_del or task_del because there's a window
220b3316ca5Sdlg * between when they come off the lists and the timeout or task code
221b3316ca5Sdlg * actually runs the respective handlers for them. mark the sensor_task
222b3316ca5Sdlg * as dying by setting period to 0 and let sensor_task_work mop up.
223b3316ca5Sdlg */
224b6582696Sdlg
225b3316ca5Sdlg rw_enter_write(&st->lock);
226b3316ca5Sdlg st->period = 0;
227b3316ca5Sdlg rw_exit_write(&st->lock);
228b6582696Sdlg }
229b6582696Sdlg
230b6582696Sdlg void
sensor_task_tick(void * arg)231abd9fc28Sdlg sensor_task_tick(void *arg)
232b6582696Sdlg {
233abd9fc28Sdlg struct sensor_task *st = arg;
234724e0567Smikeb task_add(sensors_taskq, &st->task);
235abd9fc28Sdlg }
236abd9fc28Sdlg
2371b45cbc2Sderaadt static int sensors_quiesced;
2381b45cbc2Sderaadt static int sensors_running;
2391b45cbc2Sderaadt
2401b45cbc2Sderaadt void
sensor_quiesce(void)2411b45cbc2Sderaadt sensor_quiesce(void)
2421b45cbc2Sderaadt {
2431b45cbc2Sderaadt sensors_quiesced = 1;
2441b45cbc2Sderaadt while (sensors_running > 0)
245babb761dSmpi tsleep_nsec(&sensors_running, PZERO, "sensorpause", INFSLP);
2461b45cbc2Sderaadt
2471b45cbc2Sderaadt }
2481b45cbc2Sderaadt void
sensor_restart(void)2491b45cbc2Sderaadt sensor_restart(void)
2501b45cbc2Sderaadt {
2511b45cbc2Sderaadt sensors_quiesced = 0;
2521b45cbc2Sderaadt }
2531b45cbc2Sderaadt
254abd9fc28Sdlg void
sensor_task_work(void * xst)255e4195480Sdlg sensor_task_work(void *xst)
256abd9fc28Sdlg {
257abd9fc28Sdlg struct sensor_task *st = xst;
258b3316ca5Sdlg unsigned int period = 0;
259abd9fc28Sdlg
2601b45cbc2Sderaadt atomic_inc_int(&sensors_running);
261b3316ca5Sdlg rw_enter_write(&st->lock);
262b3316ca5Sdlg period = st->period;
2631b45cbc2Sderaadt if (period > 0 && !sensors_quiesced)
264abd9fc28Sdlg st->func(st->arg);
265b3316ca5Sdlg rw_exit_write(&st->lock);
266fbfda7f8Sderaadt if (atomic_dec_int_nv(&sensors_running) == 0) {
267fbfda7f8Sderaadt if (sensors_quiesced)
2681b45cbc2Sderaadt wakeup(&sensors_running);
269fbfda7f8Sderaadt }
270ad43598bSderaadt
271b3316ca5Sdlg if (period == 0)
272a11de6bdStedu free(st, M_DEVBUF, sizeof(*st));
273b3316ca5Sdlg else
274b3316ca5Sdlg timeout_add_sec(&st->timeout, period);
275abd9fc28Sdlg }
276