1 /* $OpenBSD: kern_sensors.c,v 1.24 2010/04/20 20:49:33 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/proc.h> 22 #include <sys/systm.h> 23 #include <sys/kernel.h> 24 #include <sys/malloc.h> 25 #include <sys/queue.h> 26 #include <sys/types.h> 27 #include <sys/device.h> 28 #include <sys/hotplug.h> 29 #include <sys/timeout.h> 30 #include <sys/workq.h> 31 32 #include <sys/sensors.h> 33 #include "hotplug.h" 34 35 int sensordev_count; 36 SLIST_HEAD(, ksensordev) sensordev_list = 37 SLIST_HEAD_INITIALIZER(sensordev_list); 38 39 void 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 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 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 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 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 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 int period; 177 struct timeout timeout; 178 volatile enum { 179 ST_TICKING, 180 ST_WORKQ, 181 ST_RUNNING, 182 ST_DYING, 183 ST_DEAD 184 } state; 185 }; 186 187 void sensor_task_tick(void *); 188 void sensor_task_work(void *, void *); 189 190 struct sensor_task * 191 sensor_task_register(void *arg, void (*func)(void *), int period) 192 { 193 struct sensor_task *st; 194 195 st = malloc(sizeof(struct sensor_task), M_DEVBUF, M_NOWAIT); 196 if (st == NULL) 197 return (NULL); 198 199 st->func = func; 200 st->arg = arg; 201 st->period = period; 202 timeout_set(&st->timeout, sensor_task_tick, st); 203 204 sensor_task_tick(st); 205 206 return (st); 207 } 208 209 void 210 sensor_task_unregister(struct sensor_task *st) 211 { 212 timeout_del(&st->timeout); 213 214 switch (st->state) { 215 case ST_TICKING: 216 free(st, M_DEVBUF); 217 break; 218 219 case ST_WORKQ: 220 st->state = ST_DYING; 221 break; 222 223 case ST_RUNNING: 224 st->state = ST_DYING; 225 while (st->state != ST_DEAD) 226 tsleep(st, 0, "stunr", 0); 227 free(st, M_DEVBUF); 228 break; 229 default: 230 panic("sensor_task_unregister: unexpected state %d", 231 st->state); 232 } 233 } 234 235 void 236 sensor_task_tick(void *arg) 237 { 238 struct sensor_task *st = arg; 239 240 /* try to schedule the task */ 241 if (workq_add_task(NULL, 0, sensor_task_work, st, NULL) != 0) 242 timeout_add_msec(&st->timeout, 500); 243 244 st->state = ST_WORKQ; 245 } 246 247 void 248 sensor_task_work(void *xst, void *arg) 249 { 250 struct sensor_task *st = xst; 251 252 if (st->state == ST_DYING) { 253 free(st, M_DEVBUF); 254 return; 255 } 256 257 st->state = ST_RUNNING; 258 st->func(st->arg); 259 260 if (st->state == ST_DYING) { 261 st->state = ST_DEAD; 262 wakeup(st); 263 } else { 264 st->state = ST_TICKING; 265 timeout_add_sec(&st->timeout, st->period); 266 } 267 } 268