1 /* $OpenBSD: kern_sensors.c,v 1.36 2015/03/14 03:38:50 jsg 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/malloc.h> 23 #include <sys/queue.h> 24 #include <sys/device.h> 25 #include <sys/hotplug.h> 26 #include <sys/timeout.h> 27 #include <sys/task.h> 28 #include <sys/rwlock.h> 29 30 #include <sys/sensors.h> 31 #include "hotplug.h" 32 33 struct taskq *sensors_taskq; 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 unsigned int period; 176 struct timeout timeout; 177 struct task task; 178 struct rwlock lock; 179 }; 180 181 void sensor_task_tick(void *); 182 void sensor_task_work(void *); 183 184 struct sensor_task * 185 sensor_task_register(void *arg, void (*func)(void *), unsigned int period) 186 { 187 struct sensor_task *st; 188 189 #ifdef DIAGNOSTIC 190 if (period == 0) 191 panic("sensor_task_register: period is 0"); 192 #endif 193 194 if (sensors_taskq == NULL && 195 (sensors_taskq = taskq_create("sensors", 1, IPL_HIGH, 0)) == NULL) 196 sensors_taskq = systq; 197 198 st = malloc(sizeof(*st), M_DEVBUF, M_NOWAIT); 199 if (st == NULL) 200 return (NULL); 201 202 st->func = func; 203 st->arg = arg; 204 st->period = period; 205 timeout_set(&st->timeout, sensor_task_tick, st); 206 task_set(&st->task, sensor_task_work, st); 207 rw_init(&st->lock, "sensor"); 208 209 sensor_task_tick(st); 210 211 return (st); 212 } 213 214 void 215 sensor_task_unregister(struct sensor_task *st) 216 { 217 /* 218 * we can't reliably timeout_del or task_del because there's a window 219 * between when they come off the lists and the timeout or task code 220 * actually runs the respective handlers for them. mark the sensor_task 221 * as dying by setting period to 0 and let sensor_task_work mop up. 222 */ 223 224 rw_enter_write(&st->lock); 225 st->period = 0; 226 rw_exit_write(&st->lock); 227 } 228 229 void 230 sensor_task_tick(void *arg) 231 { 232 struct sensor_task *st = arg; 233 task_add(sensors_taskq, &st->task); 234 } 235 236 void 237 sensor_task_work(void *xst) 238 { 239 struct sensor_task *st = xst; 240 unsigned int period = 0; 241 242 rw_enter_write(&st->lock); 243 period = st->period; 244 if (period > 0) 245 st->func(st->arg); 246 rw_exit_write(&st->lock); 247 248 if (period == 0) 249 free(st, M_DEVBUF, sizeof(*st)); 250 else 251 timeout_add_sec(&st->timeout, period); 252 } 253