#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/kthread.h>
#include <sys/queue.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/spinlock.h>
#include <sys/spinlock2.h>
#include <sys/lock.h>
#include <sys/cpu_topology.h>
#include <sys/sysctl.h>
#include <sys/sensors.h>
static int sensordev_idmax;
static TAILQ_HEAD(sensordev_list, ksensordev) sensordev_list =
TAILQ_HEAD_INITIALIZER(sensordev_list);
static struct ksensordev *sensordev_get(int);
static struct ksensor *sensor_find(struct ksensordev *, enum sensor_type,
int);
struct sensor_task {
void *arg;
void (*func)(void *);
int period;
time_t nextrun;
int running;
int cpuid;
TAILQ_ENTRY(sensor_task) entry;
};
TAILQ_HEAD(sensor_tasklist, sensor_task);
struct sensor_taskthr {
struct sensor_tasklist list;
struct lock lock;
};
static void sensor_task_thread(void *);
static void sensor_task_schedule(struct sensor_taskthr *,
struct sensor_task *);
static void sensordev_sysctl_install(struct ksensordev *);
static void sensordev_sysctl_deinstall(struct ksensordev *);
static void sensor_sysctl_install(struct ksensordev *,
struct ksensor *);
static void sensor_sysctl_deinstall(struct ksensordev *,
struct ksensor *);
static struct sensor_taskthr sensor_task_threads[MAXCPU];
static int sensor_task_default_cpu;
void
sensordev_install(struct ksensordev *sensdev)
{
struct ksensordev *v, *after = NULL;
int num = 0;
SYSCTL_XLOCK();
TAILQ_FOREACH(v, &sensordev_list, list) {
if (v->num == num) {
++num;
after = v;
} else if (v->num > num) {
break;
}
}
sensdev->num = num;
if (after == NULL) {
KKASSERT(sensdev->num == 0);
TAILQ_INSERT_HEAD(&sensordev_list, sensdev, list);
} else {
TAILQ_INSERT_AFTER(&sensordev_list, after, sensdev, list);
}
sensordev_idmax = TAILQ_LAST(&sensordev_list, sensordev_list)->num + 1;
sensordev_sysctl_install(sensdev);
SYSCTL_XUNLOCK();
}
void
sensor_attach(struct ksensordev *sensdev, struct ksensor *sens)
{
struct ksensor *v, *nv;
struct ksensors_head *sh;
int i;
SYSCTL_XLOCK();
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 || nv->numt - v->numt > 1))
break;
if (v->type == sens->type)
sens->numt = v->numt + 1;
else
sens->numt = 0;
SLIST_INSERT_AFTER(v, sens, list);
}
if (sensdev->maxnumt[sens->type] == sens->numt)
sensdev->maxnumt[sens->type]++;
sensdev->sensors_count++;
sensor_sysctl_install(sensdev, sens);
SYSCTL_XUNLOCK();
}
void
sensordev_deinstall(struct ksensordev *sensdev)
{
struct ksensordev *last;
SYSCTL_XLOCK();
TAILQ_REMOVE(&sensordev_list, sensdev, list);
last = TAILQ_LAST(&sensordev_list, sensordev_list);
if (last != NULL)
sensordev_idmax = last->num + 1;
else
sensordev_idmax = 0;
sensordev_sysctl_deinstall(sensdev);
SYSCTL_XUNLOCK();
}
void
sensor_detach(struct ksensordev *sensdev, struct ksensor *sens)
{
struct ksensors_head *sh;
SYSCTL_XLOCK();
sh = &sensdev->sensors_list;
sensdev->sensors_count--;
SLIST_REMOVE(sh, sens, ksensor, list);
if (sens->numt == sensdev->maxnumt[sens->type] - 1)
sensdev->maxnumt[sens->type]--;
sensor_sysctl_deinstall(sensdev, sens);
SYSCTL_XUNLOCK();
}
static struct ksensordev *
sensordev_get(int num)
{
struct ksensordev *sd;
SYSCTL_ASSERT_LOCKED();
TAILQ_FOREACH(sd, &sensordev_list, list) {
if (sd->num == num)
return (sd);
}
return (NULL);
}
static struct ksensor *
sensor_find(struct ksensordev *sensdev, enum sensor_type type, int numt)
{
struct ksensor *s;
struct ksensors_head *sh;
SYSCTL_ASSERT_LOCKED();
sh = &sensdev->sensors_list;
SLIST_FOREACH(s, sh, list) {
if (s->type == type && s->numt == numt)
return (s);
}
return (NULL);
}
void
sensor_task_register(void *arg, void (*func)(void *), int period)
{
sensor_task_register2(arg, func, period, -1);
}
void
sensor_task_unregister(void *arg)
{
struct sensor_taskthr *thr;
struct sensor_task *st;
thr = &sensor_task_threads[sensor_task_default_cpu];
lockmgr(&thr->lock, LK_EXCLUSIVE);
TAILQ_FOREACH(st, &thr->list, entry)
if (st->arg == arg)
st->running = 0;
lockmgr(&thr->lock, LK_RELEASE);
}
void
sensor_task_unregister2(struct sensor_task *st)
{
struct sensor_taskthr *thr;
KASSERT(st->cpuid >= 0 && st->cpuid < ncpus,
("invalid task cpuid %d", st->cpuid));
thr = &sensor_task_threads[st->cpuid];
lockmgr(&thr->lock, LK_EXCLUSIVE);
st->running = 0;
lockmgr(&thr->lock, LK_RELEASE);
}
struct sensor_task *
sensor_task_register2(void *arg, void (*func)(void *), int period, int cpu)
{
struct sensor_taskthr *thr;
struct sensor_task *st;
if (cpu < 0)
cpu = sensor_task_default_cpu;
KASSERT(cpu >= 0 && cpu < ncpus, ("invalid cpuid %d", cpu));
thr = &sensor_task_threads[cpu];
st = kmalloc(sizeof(struct sensor_task), M_DEVBUF, M_WAITOK);
lockmgr(&thr->lock, LK_EXCLUSIVE);
st->arg = arg;
st->func = func;
st->period = period;
st->cpuid = cpu;
st->running = 1;
st->nextrun = 0;
TAILQ_INSERT_HEAD(&thr->list, st, entry);
wakeup(&thr->list);
lockmgr(&thr->lock, LK_RELEASE);
return st;
}
static void
sensor_task_thread(void *xthr)
{
struct sensor_taskthr *thr = xthr;
struct sensor_task *st, *nst;
time_t now;
lockmgr(&thr->lock, LK_EXCLUSIVE);
for (;;) {
while (TAILQ_EMPTY(&thr->list))
lksleep(&thr->list, &thr->lock, 0, "waittask", 0);
while ((nst = TAILQ_FIRST(&thr->list))->nextrun >
(now = time_uptime)) {
lksleep(&thr->list, &thr->lock, 0,
"timeout", (nst->nextrun - now) * hz);
}
while ((st = nst) != NULL) {
nst = TAILQ_NEXT(st, entry);
if (st->nextrun > now)
break;
TAILQ_REMOVE(&thr->list, st, entry);
if (!st->running) {
kfree(st, M_DEVBUF);
continue;
}
st->func(st->arg);
sensor_task_schedule(thr, st);
}
}
lockmgr(&thr->lock, LK_RELEASE);
}
static void
sensor_task_schedule(struct sensor_taskthr *thr, struct sensor_task *st)
{
struct sensor_task *cst;
KASSERT(lockstatus(&thr->lock, curthread) == LK_EXCLUSIVE,
("sensor task lock is not held"));
st->nextrun = time_uptime + st->period;
TAILQ_FOREACH(cst, &thr->list, entry) {
if (cst->nextrun > st->nextrun) {
TAILQ_INSERT_BEFORE(cst, st, entry);
return;
}
}
TAILQ_INSERT_TAIL(&thr->list, st, entry);
}
static int sysctl_handle_sensordev(SYSCTL_HANDLER_ARGS);
static int sysctl_handle_sensor(SYSCTL_HANDLER_ARGS);
static int sysctl_sensors_handler(SYSCTL_HANDLER_ARGS);
SYSCTL_NODE(_hw, OID_AUTO, sensors, CTLFLAG_RD, NULL,
"Hardware Sensors sysctl internal magic");
SYSCTL_NODE(_hw, HW_SENSORS, _sensors, CTLFLAG_RD, sysctl_sensors_handler,
"Hardware Sensors XP MIB interface");
SYSCTL_INT(_hw_sensors, OID_AUTO, dev_idmax, CTLFLAG_RD,
&sensordev_idmax, 0, "Max sensor device id");
static void
sensordev_sysctl_install(struct ksensordev *sensdev)
{
struct sysctl_ctx_list *cl = &sensdev->clist;
struct ksensor *s;
struct ksensors_head *sh = &sensdev->sensors_list;
SYSCTL_ASSERT_LOCKED();
KASSERT(sensdev->oid == NULL,
("sensor device %s sysctl node already installed", sensdev->xname));
sysctl_ctx_init(cl);
sensdev->oid = SYSCTL_ADD_NODE(cl, SYSCTL_STATIC_CHILDREN(_hw_sensors),
sensdev->num, sensdev->xname, CTLFLAG_RD, NULL, "");
if (sensdev->oid == NULL) {
kprintf("sensor: add sysctl tree for %s failed\n",
sensdev->xname);
return;
}
SLIST_FOREACH(s, sh, list)
sensor_sysctl_install(sensdev, s);
}
static void
sensor_sysctl_install(struct ksensordev *sensdev, struct ksensor *sens)
{
char n[32];
SYSCTL_ASSERT_LOCKED();
if (sensdev->oid == NULL) {
return;
}
ksnprintf(n, sizeof(n), "%s%d", sensor_type_s[sens->type], sens->numt);
KASSERT(sens->oid == NULL,
("sensor %s:%s sysctl node already installed", sensdev->xname, n));
sens->oid = SYSCTL_ADD_PROC(&sensdev->clist,
SYSCTL_CHILDREN(sensdev->oid), OID_AUTO, n,
CTLTYPE_STRUCT | CTLFLAG_RD, sens, 0, sysctl_handle_sensor,
"S,sensor", "");
}
static void
sensordev_sysctl_deinstall(struct ksensordev *sensdev)
{
SYSCTL_ASSERT_LOCKED();
if (sensdev->oid != NULL) {
sysctl_ctx_free(&sensdev->clist);
sensdev->oid = NULL;
}
}
static void
sensor_sysctl_deinstall(struct ksensordev *sensdev, struct ksensor *sens)
{
SYSCTL_ASSERT_LOCKED();
if (sensdev->oid != NULL && sens->oid != NULL) {
sysctl_ctx_entry_del(&sensdev->clist, sens->oid);
sysctl_remove_oid(sens->oid, 1, 0);
}
sens->oid = NULL;
}
static int
sysctl_handle_sensordev(SYSCTL_HANDLER_ARGS)
{
struct ksensordev *ksd = arg1;
struct sensordev *usd;
int error;
if (req->newptr)
return (EPERM);
usd = kmalloc(sizeof(*usd), M_TEMP, M_WAITOK | M_ZERO);
usd->num = ksd->num;
strlcpy(usd->xname, ksd->xname, sizeof(usd->xname));
memcpy(usd->maxnumt, ksd->maxnumt, sizeof(usd->maxnumt));
usd->sensors_count = ksd->sensors_count;
error = SYSCTL_OUT(req, usd, sizeof(struct sensordev));
kfree(usd, M_TEMP);
return (error);
}
static int
sysctl_handle_sensor(SYSCTL_HANDLER_ARGS)
{
struct ksensor *ks = arg1;
struct sensor *us;
int error;
if (req->newptr)
return (EPERM);
us = kmalloc(sizeof(*us), M_TEMP, M_WAITOK | M_ZERO);
memcpy(us->desc, ks->desc, sizeof(ks->desc));
us->tv = ks->tv;
us->value = ks->value;
us->type = ks->type;
us->status = ks->status;
us->numt = ks->numt;
us->flags = ks->flags;
error = SYSCTL_OUT(req, us, sizeof(struct sensor));
kfree(us, M_TEMP);
return (error);
}
static int
sysctl_sensors_handler(SYSCTL_HANDLER_ARGS)
{
int *name = arg1;
u_int namelen = arg2;
struct ksensordev *ksd;
struct ksensor *ks;
int dev, numt;
enum sensor_type type;
if (namelen != 1 && namelen != 3)
return (ENOTDIR);
dev = name[0];
if ((ksd = sensordev_get(dev)) == NULL)
return (ENOENT);
if (namelen == 1)
return (sysctl_handle_sensordev(NULL, ksd, 0, req));
type = name[1];
numt = name[2];
if ((ks = sensor_find(ksd, type, numt)) == NULL)
return (ENOENT);
return (sysctl_handle_sensor(NULL, ks, 0, req));
}
static void
sensor_sysinit(void *arg __unused)
{
const cpu_node_t *node;
int cpu;
node = get_cpu_node_by_chipid(0);
if (node != NULL && node->child_no > 0)
sensor_task_default_cpu = BSRCPUMASK(node->members);
else
sensor_task_default_cpu = ncpus - 1;
if (bootverbose) {
kprintf("sensors: tasks default to cpu%d\n",
sensor_task_default_cpu);
}
for (cpu = 0; cpu < ncpus; ++cpu) {
struct sensor_taskthr *thr = &sensor_task_threads[cpu];
int error;
TAILQ_INIT(&thr->list);
lockinit(&thr->lock, "sensorthr", 0, LK_CANRECURSE);
error = kthread_create_cpu(sensor_task_thread, thr, NULL, cpu,
"sensors %d", cpu);
if (error)
panic("sensors kthread on cpu%d failed: %d", cpu, error);
}
}
SYSINIT(sensor, SI_SUB_PRE_DRIVERS, SI_ORDER_ANY, sensor_sysinit, NULL);