1 /* $OpenBSD: kern_sensors.c,v 1.25 2013/03/28 16:58:45 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/device.h> 27 #include <sys/hotplug.h> 28 #include <sys/timeout.h> 29 #include <sys/workq.h> 30 31 #include <sys/sensors.h> 32 #include "hotplug.h" 33 34 int sensordev_count; 35 SLIST_HEAD(, ksensordev) sensordev_list = 36 SLIST_HEAD_INITIALIZER(sensordev_list); 37 38 void 39 sensordev_install(struct ksensordev *sensdev) 40 { 41 struct ksensordev *v, *nv; 42 int s; 43 44 s = splhigh(); 45 if (sensordev_count == 0) { 46 sensdev->num = 0; 47 SLIST_INSERT_HEAD(&sensordev_list, sensdev, list); 48 } else { 49 for (v = SLIST_FIRST(&sensordev_list); 50 (nv = SLIST_NEXT(v, list)) != NULL; v = nv) 51 if (nv->num - v->num > 1) 52 break; 53 sensdev->num = v->num + 1; 54 SLIST_INSERT_AFTER(v, sensdev, list); 55 } 56 sensordev_count++; 57 splx(s); 58 59 #if NHOTPLUG > 0 60 hotplug_device_attach(DV_DULL, "sensordev"); 61 #endif 62 } 63 64 void 65 sensor_attach(struct ksensordev *sensdev, struct ksensor *sens) 66 { 67 struct ksensor *v, *nv; 68 struct ksensors_head *sh; 69 int s, i; 70 71 s = splhigh(); 72 sh = &sensdev->sensors_list; 73 if (sensdev->sensors_count == 0) { 74 for (i = 0; i < SENSOR_MAX_TYPES; i++) 75 sensdev->maxnumt[i] = 0; 76 sens->numt = 0; 77 SLIST_INSERT_HEAD(sh, sens, list); 78 } else { 79 for (v = SLIST_FIRST(sh); 80 (nv = SLIST_NEXT(v, list)) != NULL; v = nv) 81 if (v->type == sens->type && (v->type != nv->type || 82 (v->type == nv->type && nv->numt - v->numt > 1))) 83 break; 84 /* sensors of the same type go after each other */ 85 if (v->type == sens->type) 86 sens->numt = v->numt + 1; 87 else 88 sens->numt = 0; 89 SLIST_INSERT_AFTER(v, sens, list); 90 } 91 /* we only increment maxnumt[] if the sensor was added 92 * to the last position of sensors of this type 93 */ 94 if (sensdev->maxnumt[sens->type] == sens->numt) 95 sensdev->maxnumt[sens->type]++; 96 sensdev->sensors_count++; 97 splx(s); 98 } 99 100 void 101 sensordev_deinstall(struct ksensordev *sensdev) 102 { 103 int s; 104 105 s = splhigh(); 106 sensordev_count--; 107 SLIST_REMOVE(&sensordev_list, sensdev, ksensordev, list); 108 splx(s); 109 110 #if NHOTPLUG > 0 111 hotplug_device_detach(DV_DULL, "sensordev"); 112 #endif 113 } 114 115 void 116 sensor_detach(struct ksensordev *sensdev, struct ksensor *sens) 117 { 118 struct ksensors_head *sh; 119 int s; 120 121 s = splhigh(); 122 sh = &sensdev->sensors_list; 123 sensdev->sensors_count--; 124 SLIST_REMOVE(sh, sens, ksensor, list); 125 /* we only decrement maxnumt[] if this is the tail 126 * sensor of this type 127 */ 128 if (sens->numt == sensdev->maxnumt[sens->type] - 1) 129 sensdev->maxnumt[sens->type]--; 130 splx(s); 131 } 132 133 int 134 sensordev_get(int num, struct ksensordev **sensdev) 135 { 136 struct ksensordev *sd; 137 138 SLIST_FOREACH(sd, &sensordev_list, list) { 139 if (sd->num == num) { 140 *sensdev = sd; 141 return (0); 142 } 143 if (sd->num > num) 144 return (ENXIO); 145 } 146 return (ENOENT); 147 } 148 149 int 150 sensor_find(int dev, enum sensor_type type, int numt, struct ksensor **ksensorp) 151 { 152 struct ksensor *s; 153 struct ksensordev *sensdev; 154 struct ksensors_head *sh; 155 int ret; 156 157 ret = sensordev_get(dev, &sensdev); 158 if (ret) 159 return (ret); 160 161 sh = &sensdev->sensors_list; 162 SLIST_FOREACH(s, sh, list) 163 if (s->type == type && s->numt == numt) { 164 *ksensorp = s; 165 return (0); 166 } 167 168 return (ENOENT); 169 } 170 171 struct sensor_task { 172 void (*func)(void *); 173 void *arg; 174 175 int period; 176 struct timeout timeout; 177 volatile enum { 178 ST_TICKING, 179 ST_WORKQ, 180 ST_RUNNING, 181 ST_DYING, 182 ST_DEAD 183 } state; 184 }; 185 186 void sensor_task_tick(void *); 187 void sensor_task_work(void *, void *); 188 189 struct sensor_task * 190 sensor_task_register(void *arg, void (*func)(void *), int period) 191 { 192 struct sensor_task *st; 193 194 st = malloc(sizeof(struct sensor_task), M_DEVBUF, M_NOWAIT); 195 if (st == NULL) 196 return (NULL); 197 198 st->func = func; 199 st->arg = arg; 200 st->period = period; 201 timeout_set(&st->timeout, sensor_task_tick, st); 202 203 sensor_task_tick(st); 204 205 return (st); 206 } 207 208 void 209 sensor_task_unregister(struct sensor_task *st) 210 { 211 timeout_del(&st->timeout); 212 213 switch (st->state) { 214 case ST_TICKING: 215 free(st, M_DEVBUF); 216 break; 217 218 case ST_WORKQ: 219 st->state = ST_DYING; 220 break; 221 222 case ST_RUNNING: 223 st->state = ST_DYING; 224 while (st->state != ST_DEAD) 225 tsleep(st, 0, "stunr", 0); 226 free(st, M_DEVBUF); 227 break; 228 default: 229 panic("sensor_task_unregister: unexpected state %d", 230 st->state); 231 } 232 } 233 234 void 235 sensor_task_tick(void *arg) 236 { 237 struct sensor_task *st = arg; 238 239 /* try to schedule the task */ 240 if (workq_add_task(NULL, 0, sensor_task_work, st, NULL) != 0) 241 timeout_add_msec(&st->timeout, 500); 242 243 st->state = ST_WORKQ; 244 } 245 246 void 247 sensor_task_work(void *xst, void *arg) 248 { 249 struct sensor_task *st = xst; 250 251 if (st->state == ST_DYING) { 252 free(st, M_DEVBUF); 253 return; 254 } 255 256 st->state = ST_RUNNING; 257 st->func(st->arg); 258 259 if (st->state == ST_DYING) { 260 st->state = ST_DEAD; 261 wakeup(st); 262 } else { 263 st->state = ST_TICKING; 264 timeout_add_sec(&st->timeout, st->period); 265 } 266 } 267