xref: /openbsd-src/sys/kern/kern_sensors.c (revision 2b46a8cbba384f799321fc554d4d1c95563ea0a2)
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