1 /* $OpenBSD: kern_sensors.c,v 1.18 2007/06/01 04:15:45 dlg 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/systm.h> 22 #include <sys/kernel.h> 23 #include <sys/malloc.h> 24 #include <sys/kthread.h> 25 #include <sys/queue.h> 26 #include <sys/types.h> 27 #include <sys/time.h> 28 #include <sys/device.h> 29 #include <sys/hotplug.h> 30 #include <sys/workq.h> 31 32 #include <sys/sensors.h> 33 #include "hotplug.h" 34 35 int sensordev_count = 0; 36 SLIST_HEAD(, ksensordev) sensordev_list = SLIST_HEAD_INITIALIZER(sensordev_list); 37 38 struct sensor_task { 39 void (*func)(void *); 40 void *arg; 41 42 int period; 43 time_t nextrun; 44 TAILQ_ENTRY(sensor_task) entry; 45 }; 46 47 struct timeout sensor_task_to; 48 TAILQ_HEAD(, sensor_task) sensor_task_list = 49 TAILQ_HEAD_INITIALIZER(sensor_task_list); 50 51 void sensor_task_tick(void *); 52 void sensor_task_schedule(struct sensor_task *); 53 void sensor_task_work(void *, void *); 54 55 void 56 sensordev_install(struct ksensordev *sensdev) 57 { 58 struct ksensordev *v, *nv; 59 int s; 60 61 s = splhigh(); 62 if (sensordev_count == 0) { 63 sensdev->num = 0; 64 SLIST_INSERT_HEAD(&sensordev_list, sensdev, list); 65 } else { 66 for (v = SLIST_FIRST(&sensordev_list); 67 (nv = SLIST_NEXT(v, list)) != NULL; v = nv) 68 if (nv->num - v->num > 1) 69 break; 70 sensdev->num = v->num + 1; 71 SLIST_INSERT_AFTER(v, sensdev, list); 72 } 73 sensordev_count++; 74 splx(s); 75 76 #if NHOTPLUG > 0 77 hotplug_device_attach(DV_DULL, "sensordev"); 78 #endif 79 } 80 81 void 82 sensor_attach(struct ksensordev *sensdev, struct ksensor *sens) 83 { 84 struct ksensor *v, *nv; 85 struct ksensors_head *sh; 86 int s, i; 87 88 s = splhigh(); 89 sh = &sensdev->sensors_list; 90 if (sensdev->sensors_count == 0) { 91 for (i = 0; i < SENSOR_MAX_TYPES; i++) 92 sensdev->maxnumt[i] = 0; 93 sens->numt = 0; 94 SLIST_INSERT_HEAD(sh, sens, list); 95 } else { 96 for (v = SLIST_FIRST(sh); 97 (nv = SLIST_NEXT(v, list)) != NULL; v = nv) 98 if (v->type == sens->type && (v->type != nv->type || 99 (v->type == nv->type && nv->numt - v->numt > 1))) 100 break; 101 /* sensors of the same type go after each other */ 102 if (v->type == sens->type) 103 sens->numt = v->numt + 1; 104 else 105 sens->numt = 0; 106 SLIST_INSERT_AFTER(v, sens, list); 107 } 108 /* we only increment maxnumt[] if the sensor was added 109 * to the last position of sensors of this type 110 */ 111 if (sensdev->maxnumt[sens->type] == sens->numt) 112 sensdev->maxnumt[sens->type]++; 113 sensdev->sensors_count++; 114 splx(s); 115 } 116 117 void 118 sensordev_deinstall(struct ksensordev *sensdev) 119 { 120 int s; 121 122 s = splhigh(); 123 sensordev_count--; 124 SLIST_REMOVE(&sensordev_list, sensdev, ksensordev, list); 125 splx(s); 126 127 #if NHOTPLUG > 0 128 hotplug_device_detach(DV_DULL, "sensordev"); 129 #endif 130 } 131 132 void 133 sensor_detach(struct ksensordev *sensdev, struct ksensor *sens) 134 { 135 struct ksensors_head *sh; 136 int s; 137 138 s = splhigh(); 139 sh = &sensdev->sensors_list; 140 sensdev->sensors_count--; 141 SLIST_REMOVE(sh, sens, ksensor, list); 142 /* we only decrement maxnumt[] if this is the tail 143 * sensor of this type 144 */ 145 if (sens->numt == sensdev->maxnumt[sens->type] - 1) 146 sensdev->maxnumt[sens->type]--; 147 splx(s); 148 } 149 150 struct ksensordev * 151 sensordev_get(int num) 152 { 153 struct ksensordev *sd; 154 155 SLIST_FOREACH(sd, &sensordev_list, list) 156 if (sd->num == num) 157 return (sd); 158 159 return (NULL); 160 } 161 162 struct ksensor * 163 sensor_find(int dev, enum sensor_type type, int numt) 164 { 165 struct ksensor *s; 166 struct ksensordev *sensdev; 167 struct ksensors_head *sh; 168 169 sensdev = sensordev_get(dev); 170 if (sensdev == NULL) 171 return (NULL); 172 173 sh = &sensdev->sensors_list; 174 SLIST_FOREACH(s, sh, list) 175 if (s->type == type && s->numt == numt) 176 return (s); 177 178 return (NULL); 179 } 180 181 int 182 sensor_task_register(void *arg, void (*func)(void *), int period) 183 { 184 struct sensor_task *st; 185 186 st = malloc(sizeof(struct sensor_task), M_DEVBUF, M_NOWAIT); 187 if (st == NULL) 188 return (1); 189 190 st->arg = arg; 191 st->func = func; 192 st->period = period; 193 st->nextrun = 0; 194 195 if (TAILQ_EMPTY(&sensor_task_list)) 196 timeout_set(&sensor_task_to, sensor_task_tick, NULL); 197 else 198 timeout_del(&sensor_task_to); 199 200 TAILQ_INSERT_HEAD(&sensor_task_list, st, entry); 201 202 sensor_task_tick(NULL); 203 204 return (0); 205 } 206 207 void 208 sensor_task_unregister(void *arg) 209 { 210 struct sensor_task *st, *nst; 211 212 timeout_del(&sensor_task_to); 213 214 nst = TAILQ_FIRST(&sensor_task_list); 215 while (nst != NULL) { 216 st = nst; 217 nst = TAILQ_NEXT(st, entry); 218 219 if (st->arg == arg) 220 free(st, M_DEVBUF); 221 } 222 223 if (TAILQ_EMPTY(&sensor_task_list)) 224 return; 225 226 sensor_task_tick(NULL); 227 } 228 229 void 230 sensor_task_tick(void *arg) 231 { 232 struct sensor_task *st, *nst; 233 time_t now = time_uptime; 234 235 #ifdef DIAGNOSTIC 236 if (TAILQ_EMPTY(&sensor_task_list)) 237 panic("sensor task tick for no sensors"); 238 #endif 239 240 nst = TAILQ_FIRST(&sensor_task_list); 241 while (nst->nextrun <= now) { 242 st = nst; 243 nst = TAILQ_NEXT(st, entry); 244 245 /* try to schedule the task */ 246 if (workq_add_task(NULL, 0, sensor_task_work, st, NULL) != 0) { 247 timeout_add(&sensor_task_to, hz); 248 return; 249 } 250 251 /* take it out while we work on it */ 252 TAILQ_REMOVE(&sensor_task_list, st, entry); 253 254 if (nst == NULL) 255 return; 256 } 257 258 timeout_add(&sensor_task_to, (nst->nextrun - now) * hz); 259 } 260 261 void 262 sensor_task_work(void *xst, void *arg) 263 { 264 struct sensor_task *st = xst; 265 266 timeout_del(&sensor_task_to); 267 268 st->func(st->arg); 269 sensor_task_schedule(st); 270 271 sensor_task_tick(NULL); 272 } 273 274 void 275 sensor_task_schedule(struct sensor_task *st) 276 { 277 struct sensor_task *cst; 278 279 st->nextrun = time_uptime + st->period; 280 281 TAILQ_FOREACH(cst, &sensor_task_list, entry) { 282 if (cst->nextrun > st->nextrun) { 283 TAILQ_INSERT_BEFORE(cst, st, entry); 284 return; 285 } 286 } 287 288 /* must be an empty list, or at the end of the list */ 289 TAILQ_INSERT_TAIL(&sensor_task_list, st, entry); 290 } 291