1 /* $OpenBSD: kern_sensors.c,v 1.27 2013/12/09 17:39:08 mikeb 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/task.h> 30 #include <sys/rwlock.h> 31 32 #include <sys/sensors.h> 33 #include "hotplug.h" 34 35 struct taskq *sensors_taskq; 36 int sensordev_count; 37 SLIST_HEAD(, ksensordev) sensordev_list = 38 SLIST_HEAD_INITIALIZER(sensordev_list); 39 40 void 41 sensordev_install(struct ksensordev *sensdev) 42 { 43 struct ksensordev *v, *nv; 44 int s; 45 46 s = splhigh(); 47 if (sensordev_count == 0) { 48 sensdev->num = 0; 49 SLIST_INSERT_HEAD(&sensordev_list, sensdev, list); 50 } else { 51 for (v = SLIST_FIRST(&sensordev_list); 52 (nv = SLIST_NEXT(v, list)) != NULL; v = nv) 53 if (nv->num - v->num > 1) 54 break; 55 sensdev->num = v->num + 1; 56 SLIST_INSERT_AFTER(v, sensdev, list); 57 } 58 sensordev_count++; 59 splx(s); 60 61 #if NHOTPLUG > 0 62 hotplug_device_attach(DV_DULL, "sensordev"); 63 #endif 64 } 65 66 void 67 sensor_attach(struct ksensordev *sensdev, struct ksensor *sens) 68 { 69 struct ksensor *v, *nv; 70 struct ksensors_head *sh; 71 int s, i; 72 73 s = splhigh(); 74 sh = &sensdev->sensors_list; 75 if (sensdev->sensors_count == 0) { 76 for (i = 0; i < SENSOR_MAX_TYPES; i++) 77 sensdev->maxnumt[i] = 0; 78 sens->numt = 0; 79 SLIST_INSERT_HEAD(sh, sens, list); 80 } else { 81 for (v = SLIST_FIRST(sh); 82 (nv = SLIST_NEXT(v, list)) != NULL; v = nv) 83 if (v->type == sens->type && (v->type != nv->type || 84 (v->type == nv->type && nv->numt - v->numt > 1))) 85 break; 86 /* sensors of the same type go after each other */ 87 if (v->type == sens->type) 88 sens->numt = v->numt + 1; 89 else 90 sens->numt = 0; 91 SLIST_INSERT_AFTER(v, sens, list); 92 } 93 /* we only increment maxnumt[] if the sensor was added 94 * to the last position of sensors of this type 95 */ 96 if (sensdev->maxnumt[sens->type] == sens->numt) 97 sensdev->maxnumt[sens->type]++; 98 sensdev->sensors_count++; 99 splx(s); 100 } 101 102 void 103 sensordev_deinstall(struct ksensordev *sensdev) 104 { 105 int s; 106 107 s = splhigh(); 108 sensordev_count--; 109 SLIST_REMOVE(&sensordev_list, sensdev, ksensordev, list); 110 splx(s); 111 112 #if NHOTPLUG > 0 113 hotplug_device_detach(DV_DULL, "sensordev"); 114 #endif 115 } 116 117 void 118 sensor_detach(struct ksensordev *sensdev, struct ksensor *sens) 119 { 120 struct ksensors_head *sh; 121 int s; 122 123 s = splhigh(); 124 sh = &sensdev->sensors_list; 125 sensdev->sensors_count--; 126 SLIST_REMOVE(sh, sens, ksensor, list); 127 /* we only decrement maxnumt[] if this is the tail 128 * sensor of this type 129 */ 130 if (sens->numt == sensdev->maxnumt[sens->type] - 1) 131 sensdev->maxnumt[sens->type]--; 132 splx(s); 133 } 134 135 int 136 sensordev_get(int num, struct ksensordev **sensdev) 137 { 138 struct ksensordev *sd; 139 140 SLIST_FOREACH(sd, &sensordev_list, list) { 141 if (sd->num == num) { 142 *sensdev = sd; 143 return (0); 144 } 145 if (sd->num > num) 146 return (ENXIO); 147 } 148 return (ENOENT); 149 } 150 151 int 152 sensor_find(int dev, enum sensor_type type, int numt, struct ksensor **ksensorp) 153 { 154 struct ksensor *s; 155 struct ksensordev *sensdev; 156 struct ksensors_head *sh; 157 int ret; 158 159 ret = sensordev_get(dev, &sensdev); 160 if (ret) 161 return (ret); 162 163 sh = &sensdev->sensors_list; 164 SLIST_FOREACH(s, sh, list) 165 if (s->type == type && s->numt == numt) { 166 *ksensorp = s; 167 return (0); 168 } 169 170 return (ENOENT); 171 } 172 173 struct sensor_task { 174 void (*func)(void *); 175 void *arg; 176 177 unsigned int period; 178 struct timeout timeout; 179 struct task task; 180 struct rwlock lock; 181 }; 182 183 void sensor_task_tick(void *); 184 void sensor_task_work(void *, void *); 185 186 struct sensor_task * 187 sensor_task_register(void *arg, void (*func)(void *), unsigned int period) 188 { 189 struct sensor_task *st; 190 191 #ifdef DIAGNOSTIC 192 if (period == 0) 193 panic("sensor_task_register: period is 0"); 194 #endif 195 196 if (sensors_taskq == NULL && 197 (sensors_taskq = taskq_create("sensors", 1, IPL_HIGH)) == NULL) 198 sensors_taskq = systq; 199 200 st = malloc(sizeof(struct sensor_task), M_DEVBUF, M_NOWAIT); 201 if (st == NULL) 202 return (NULL); 203 204 st->func = func; 205 st->arg = arg; 206 st->period = period; 207 timeout_set(&st->timeout, sensor_task_tick, st); 208 task_set(&st->task, sensor_task_work, st, NULL); 209 rw_init(&st->lock, "sensor"); 210 211 sensor_task_tick(st); 212 213 return (st); 214 } 215 216 void 217 sensor_task_unregister(struct sensor_task *st) 218 { 219 /* 220 * we can't reliably timeout_del or task_del because there's a window 221 * between when they come off the lists and the timeout or task code 222 * actually runs the respective handlers for them. mark the sensor_task 223 * as dying by setting period to 0 and let sensor_task_work mop up. 224 */ 225 226 rw_enter_write(&st->lock); 227 st->period = 0; 228 rw_exit_write(&st->lock); 229 } 230 231 void 232 sensor_task_tick(void *arg) 233 { 234 struct sensor_task *st = arg; 235 task_add(sensors_taskq, &st->task); 236 } 237 238 void 239 sensor_task_work(void *xst, void *arg) 240 { 241 struct sensor_task *st = xst; 242 unsigned int period = 0; 243 244 rw_enter_write(&st->lock); 245 period = st->period; 246 if (period > 0) 247 st->func(st->arg); 248 rw_exit_write(&st->lock); 249 250 if (period == 0) 251 free(st, M_DEVBUF); 252 else 253 timeout_add_sec(&st->timeout, period); 254 } 255