/* $OpenBSD: kern_sensors.c,v 1.38 2017/04/09 15:47:18 deraadt Exp $ */ /* * Copyright (c) 2005 David Gwynne * Copyright (c) 2006 Constantine A. Murenin * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include "hotplug.h" struct taskq *sensors_taskq; int sensordev_count; SLIST_HEAD(, ksensordev) sensordev_list = SLIST_HEAD_INITIALIZER(sensordev_list); void sensordev_install(struct ksensordev *sensdev) { struct ksensordev *v, *nv; int s; s = splhigh(); if (sensordev_count == 0) { sensdev->num = 0; SLIST_INSERT_HEAD(&sensordev_list, sensdev, list); } else { for (v = SLIST_FIRST(&sensordev_list); (nv = SLIST_NEXT(v, list)) != NULL; v = nv) if (nv->num - v->num > 1) break; sensdev->num = v->num + 1; SLIST_INSERT_AFTER(v, sensdev, list); } sensordev_count++; splx(s); #if NHOTPLUG > 0 hotplug_device_attach(DV_DULL, "sensordev"); #endif } void sensor_attach(struct ksensordev *sensdev, struct ksensor *sens) { struct ksensor *v, *nv; struct ksensors_head *sh; int s, i; s = splhigh(); sh = &sensdev->sensors_list; if (sensdev->sensors_count == 0) { for (i = 0; i < SENSOR_MAX_TYPES; i++) sensdev->maxnumt[i] = 0; sens->numt = 0; SLIST_INSERT_HEAD(sh, sens, list); } else { for (v = SLIST_FIRST(sh); (nv = SLIST_NEXT(v, list)) != NULL; v = nv) if (v->type == sens->type && (v->type != nv->type || (v->type == nv->type && nv->numt - v->numt > 1))) break; /* sensors of the same type go after each other */ if (v->type == sens->type) sens->numt = v->numt + 1; else sens->numt = 0; SLIST_INSERT_AFTER(v, sens, list); } /* we only increment maxnumt[] if the sensor was added * to the last position of sensors of this type */ if (sensdev->maxnumt[sens->type] == sens->numt) sensdev->maxnumt[sens->type]++; sensdev->sensors_count++; splx(s); } void sensordev_deinstall(struct ksensordev *sensdev) { int s; s = splhigh(); sensordev_count--; SLIST_REMOVE(&sensordev_list, sensdev, ksensordev, list); splx(s); #if NHOTPLUG > 0 hotplug_device_detach(DV_DULL, "sensordev"); #endif } void sensor_detach(struct ksensordev *sensdev, struct ksensor *sens) { struct ksensors_head *sh; int s; s = splhigh(); sh = &sensdev->sensors_list; sensdev->sensors_count--; SLIST_REMOVE(sh, sens, ksensor, list); /* we only decrement maxnumt[] if this is the tail * sensor of this type */ if (sens->numt == sensdev->maxnumt[sens->type] - 1) sensdev->maxnumt[sens->type]--; splx(s); } int sensordev_get(int num, struct ksensordev **sensdev) { struct ksensordev *sd; SLIST_FOREACH(sd, &sensordev_list, list) { if (sd->num == num) { *sensdev = sd; return (0); } if (sd->num > num) return (ENXIO); } return (ENOENT); } int sensor_find(int dev, enum sensor_type type, int numt, struct ksensor **ksensorp) { struct ksensor *s; struct ksensordev *sensdev; struct ksensors_head *sh; int ret; ret = sensordev_get(dev, &sensdev); if (ret) return (ret); sh = &sensdev->sensors_list; SLIST_FOREACH(s, sh, list) if (s->type == type && s->numt == numt) { *ksensorp = s; return (0); } return (ENOENT); } struct sensor_task { void (*func)(void *); void *arg; unsigned int period; struct timeout timeout; struct task task; struct rwlock lock; }; void sensor_task_tick(void *); void sensor_task_work(void *); struct sensor_task * sensor_task_register(void *arg, void (*func)(void *), unsigned int period) { struct sensor_task *st; #ifdef DIAGNOSTIC if (period == 0) panic("sensor_task_register: period is 0"); #endif if (sensors_taskq == NULL && (sensors_taskq = taskq_create("sensors", 1, IPL_HIGH, 0)) == NULL) sensors_taskq = systq; st = malloc(sizeof(*st), M_DEVBUF, M_NOWAIT); if (st == NULL) return (NULL); st->func = func; st->arg = arg; st->period = period; timeout_set(&st->timeout, sensor_task_tick, st); task_set(&st->task, sensor_task_work, st); rw_init(&st->lock, "sensor"); sensor_task_tick(st); return (st); } void sensor_task_unregister(struct sensor_task *st) { /* * we can't reliably timeout_del or task_del because there's a window * between when they come off the lists and the timeout or task code * actually runs the respective handlers for them. mark the sensor_task * as dying by setting period to 0 and let sensor_task_work mop up. */ rw_enter_write(&st->lock); st->period = 0; rw_exit_write(&st->lock); } void sensor_task_tick(void *arg) { struct sensor_task *st = arg; task_add(sensors_taskq, &st->task); } static int sensors_quiesced; static int sensors_running; void sensor_quiesce(void) { sensors_quiesced = 1; while (sensors_running > 0) tsleep(&sensors_running, PZERO, "sensorpause", 0); } void sensor_restart(void) { sensors_quiesced = 0; } void sensor_task_work(void *xst) { struct sensor_task *st = xst; unsigned int period = 0; atomic_inc_int(&sensors_running); rw_enter_write(&st->lock); period = st->period; if (period > 0 && !sensors_quiesced) st->func(st->arg); rw_exit_write(&st->lock); if (atomic_dec_int_nv(&sensors_running) == 0) { if (sensors_quiesced) wakeup(&sensors_running); } if (period == 0) free(st, M_DEVBUF, sizeof(*st)); else timeout_add_sec(&st->timeout, period); }