1 /* $OpenBSD: kern_sensors.c,v 1.22 2008/09/10 14:01:23 blambert 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 = 0; 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 struct ksensordev * 135 sensordev_get(int num) 136 { 137 struct ksensordev *sd; 138 139 SLIST_FOREACH(sd, &sensordev_list, list) 140 if (sd->num == num) 141 return (sd); 142 143 return (NULL); 144 } 145 146 struct ksensor * 147 sensor_find(int dev, enum sensor_type type, int numt) 148 { 149 struct ksensor *s; 150 struct ksensordev *sensdev; 151 struct ksensors_head *sh; 152 153 sensdev = sensordev_get(dev); 154 if (sensdev == NULL) 155 return (NULL); 156 157 sh = &sensdev->sensors_list; 158 SLIST_FOREACH(s, sh, list) 159 if (s->type == type && s->numt == numt) 160 return (s); 161 162 return (NULL); 163 } 164 165 struct sensor_task { 166 void (*func)(void *); 167 void *arg; 168 169 int period; 170 struct timeout timeout; 171 volatile enum { 172 ST_TICKING, 173 ST_WORKQ, 174 ST_RUNNING, 175 ST_DYING, 176 ST_DEAD 177 } state; 178 }; 179 180 void sensor_task_tick(void *); 181 void sensor_task_work(void *, void *); 182 183 struct sensor_task * 184 sensor_task_register(void *arg, void (*func)(void *), int period) 185 { 186 struct sensor_task *st; 187 188 st = malloc(sizeof(struct sensor_task), M_DEVBUF, M_NOWAIT); 189 if (st == NULL) 190 return (NULL); 191 192 st->func = func; 193 st->arg = arg; 194 st->period = period; 195 timeout_set(&st->timeout, sensor_task_tick, st); 196 197 sensor_task_tick(st); 198 199 return (st); 200 } 201 202 void 203 sensor_task_unregister(struct sensor_task *st) 204 { 205 timeout_del(&st->timeout); 206 207 switch (st->state) { 208 case ST_TICKING: 209 free(st, M_DEVBUF); 210 break; 211 212 case ST_WORKQ: 213 st->state = ST_DYING; 214 break; 215 216 case ST_RUNNING: 217 st->state = ST_DYING; 218 while (st->state != ST_DEAD) 219 tsleep(st, 0, "stunr", 0); 220 free(st, M_DEVBUF); 221 break; 222 default: 223 panic("sensor_task_unregister: unexpected state %d", 224 st->state); 225 } 226 } 227 228 void 229 sensor_task_tick(void *arg) 230 { 231 struct sensor_task *st = arg; 232 233 /* try to schedule the task */ 234 if (workq_add_task(NULL, 0, sensor_task_work, st, NULL) != 0) 235 timeout_add(&st->timeout, hz/2); 236 237 st->state = ST_WORKQ; 238 } 239 240 void 241 sensor_task_work(void *xst, void *arg) 242 { 243 struct sensor_task *st = xst; 244 245 if (st->state == ST_DYING) { 246 free(st, M_DEVBUF); 247 return; 248 } 249 250 st->state = ST_RUNNING; 251 st->func(st->arg); 252 253 if (st->state == ST_DYING) { 254 st->state = ST_DEAD; 255 wakeup(st); 256 } else { 257 st->state = ST_TICKING; 258 timeout_add_sec(&st->timeout, st->period); 259 } 260 } 261