1 /* $OpenBSD: kern_sensors.c,v 1.19 2007/06/04 18:42:05 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/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/lock.h> 29 30 #include <sys/sysctl.h> 31 #include <sys/sensors.h> 32 33 int sensordev_count = 0; 34 SLIST_HEAD(, ksensordev) sensordev_list = SLIST_HEAD_INITIALIZER(sensordev_list); 35 36 struct ksensordev *sensordev_get(int); 37 struct ksensor *sensor_find(struct ksensordev *, enum sensor_type, int); 38 39 struct sensor_task { 40 void *arg; 41 void (*func)(void *); 42 43 int period; 44 time_t nextrun; 45 volatile int running; 46 TAILQ_ENTRY(sensor_task) entry; 47 }; 48 49 void sensor_task_thread(void *); 50 void sensor_task_schedule(struct sensor_task *); 51 52 TAILQ_HEAD(, sensor_task) tasklist = TAILQ_HEAD_INITIALIZER(tasklist); 53 54 #ifndef NOSYSCTL8HACK 55 void sensor_sysctl8magic_install(struct ksensordev *); 56 void sensor_sysctl8magic_deinstall(struct ksensordev *); 57 #endif 58 59 void 60 sensordev_install(struct ksensordev *sensdev) 61 { 62 struct ksensordev *v, *nv; 63 64 /* mtx_lock(&Giant); */ 65 if (sensordev_count == 0) { 66 sensdev->num = 0; 67 SLIST_INSERT_HEAD(&sensordev_list, sensdev, list); 68 } else { 69 for (v = SLIST_FIRST(&sensordev_list); 70 (nv = SLIST_NEXT(v, list)) != NULL; v = nv) 71 if (nv->num - v->num > 1) 72 break; 73 sensdev->num = v->num + 1; 74 SLIST_INSERT_AFTER(v, sensdev, list); 75 } 76 sensordev_count++; 77 /* mtx_unlock(&Giant); */ 78 79 #ifndef NOSYSCTL8HACK 80 sensor_sysctl8magic_install(sensdev); 81 #endif 82 } 83 84 void 85 sensor_attach(struct ksensordev *sensdev, struct ksensor *sens) 86 { 87 struct ksensor *v, *nv; 88 struct ksensors_head *sh; 89 int i; 90 91 /* mtx_lock(&Giant); */ 92 sh = &sensdev->sensors_list; 93 if (sensdev->sensors_count == 0) { 94 for (i = 0; i < SENSOR_MAX_TYPES; i++) 95 sensdev->maxnumt[i] = 0; 96 sens->numt = 0; 97 SLIST_INSERT_HEAD(sh, sens, list); 98 } else { 99 for (v = SLIST_FIRST(sh); 100 (nv = SLIST_NEXT(v, list)) != NULL; v = nv) 101 if (v->type == sens->type && (v->type != nv->type || 102 (v->type == nv->type && nv->numt - v->numt > 1))) 103 break; 104 /* sensors of the same type go after each other */ 105 if (v->type == sens->type) 106 sens->numt = v->numt + 1; 107 else 108 sens->numt = 0; 109 SLIST_INSERT_AFTER(v, sens, list); 110 } 111 /* we only increment maxnumt[] if the sensor was added 112 * to the last position of sensors of this type 113 */ 114 if (sensdev->maxnumt[sens->type] == sens->numt) 115 sensdev->maxnumt[sens->type]++; 116 sensdev->sensors_count++; 117 /* mtx_unlock(&Giant); */ 118 } 119 120 void 121 sensordev_deinstall(struct ksensordev *sensdev) 122 { 123 /* mtx_lock(&Giant); */ 124 sensordev_count--; 125 SLIST_REMOVE(&sensordev_list, sensdev, ksensordev, list); 126 /* mtx_unlock(&Giant); */ 127 128 #ifndef NOSYSCTL8HACK 129 sensor_sysctl8magic_deinstall(sensdev); 130 #endif 131 } 132 133 void 134 sensor_detach(struct ksensordev *sensdev, struct ksensor *sens) 135 { 136 struct ksensors_head *sh; 137 138 /* mtx_lock(&Giant); */ 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 /* mtx_unlock(&Giant); */ 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(struct ksensordev *sensdev, enum sensor_type type, int numt) 164 { 165 struct ksensor *s; 166 struct ksensors_head *sh; 167 168 sh = &sensdev->sensors_list; 169 SLIST_FOREACH(s, sh, list) 170 if (s->type == type && s->numt == numt) 171 return (s); 172 173 return (NULL); 174 } 175 176 int 177 sensor_task_register(void *arg, void (*func)(void *), int period) 178 { 179 struct sensor_task *st; 180 int create_thread = 0; 181 182 st = kmalloc(sizeof(struct sensor_task), M_DEVBUF, M_NOWAIT); 183 if (st == NULL) 184 return (1); 185 186 st->arg = arg; 187 st->func = func; 188 st->period = period; 189 190 st->running = 1; 191 192 if (TAILQ_EMPTY(&tasklist)) 193 create_thread = 1; 194 195 st->nextrun = 0; 196 TAILQ_INSERT_HEAD(&tasklist, st, entry); 197 198 if (create_thread) 199 if (kthread_create(sensor_task_thread, NULL, NULL, 0, 0, 200 "sensors") != 0) 201 panic("sensors kthread"); 202 203 wakeup(&tasklist); 204 205 return (0); 206 } 207 208 void 209 sensor_task_unregister(void *arg) 210 { 211 struct sensor_task *st; 212 213 TAILQ_FOREACH(st, &tasklist, entry) 214 if (st->arg == arg) 215 st->running = 0; 216 } 217 218 void 219 sensor_task_thread(void *arg) 220 { 221 struct sensor_task *st, *nst; 222 time_t now; 223 224 while (!TAILQ_EMPTY(&tasklist)) { 225 while ((nst = TAILQ_FIRST(&tasklist))->nextrun > 226 (now = time_second)) 227 tsleep(&tasklist, 0, "timeout", 228 (nst->nextrun - now) * hz); 229 230 while ((st = nst) != NULL) { 231 nst = TAILQ_NEXT(st, entry); 232 233 if (st->nextrun > now) 234 break; 235 236 /* take it out while we work on it */ 237 TAILQ_REMOVE(&tasklist, st, entry); 238 239 if (!st->running) { 240 kfree(st, M_DEVBUF); 241 continue; 242 } 243 244 /* run the task */ 245 st->func(st->arg); 246 /* stick it back in the tasklist */ 247 sensor_task_schedule(st); 248 } 249 } 250 251 kthread_exit(); 252 } 253 254 void 255 sensor_task_schedule(struct sensor_task *st) 256 { 257 struct sensor_task *cst; 258 259 st->nextrun = time_second + st->period; 260 261 TAILQ_FOREACH(cst, &tasklist, entry) { 262 if (cst->nextrun > st->nextrun) { 263 TAILQ_INSERT_BEFORE(cst, st, entry); 264 return; 265 } 266 } 267 268 /* must be an empty list, or at the end of the list */ 269 TAILQ_INSERT_TAIL(&tasklist, st, entry); 270 } 271 272 /* 273 * sysctl glue code 274 */ 275 int sysctl_handle_sensordev(SYSCTL_HANDLER_ARGS); 276 int sysctl_handle_sensor(SYSCTL_HANDLER_ARGS); 277 int sysctl_sensors_handler(SYSCTL_HANDLER_ARGS); 278 279 #ifndef NOSYSCTL8HACK 280 281 SYSCTL_NODE(_hw, OID_AUTO, sensors, CTLFLAG_RD, NULL, 282 "Hardware Sensors sysctl internal magic"); 283 SYSCTL_NODE(_hw, HW_SENSORS, _sensors, CTLFLAG_RD, sysctl_sensors_handler, 284 "Hardware Sensors XP MIB interface"); 285 286 #else /* NOSYSCTL8HACK */ 287 288 SYSCTL_NODE(_hw, HW_SENSORS, sensors, CTLFLAG_RD, sysctl_sensors_handler, 289 "Hardware Sensors"); 290 int sensors_debug = 1; 291 SYSCTL_INT(_hw_sensors, OID_AUTO, debug, CTLFLAG_RD, &sensors_debug, 0, "sensors debug"); 292 293 #endif /* !NOSYSCTL8HACK */ 294 295 296 #ifndef NOSYSCTL8HACK 297 298 /* 299 * XXX: 300 * FreeBSD's sysctl(9) .oid_handler functionality is not accustomed 301 * for the CTLTYPE_NODE handler to handle the undocumented sysctl 302 * magic calls. As soon as such functionality is developed, 303 * sysctl_sensors_handler() should be converted to handle all such 304 * calls, and these sysctl_add_oid(9) calls should be removed 305 * "with a big axe". This whole sysctl_add_oid(9) business is solely 306 * to please sysctl(8). 307 */ 308 309 void 310 sensor_sysctl8magic_install(struct ksensordev *sensdev) 311 { 312 struct sysctl_oid_list *ol; 313 struct sysctl_ctx_list *cl = &sensdev->clist; 314 struct ksensor *s; 315 struct ksensors_head *sh = &sensdev->sensors_list; 316 317 sysctl_ctx_init(cl); 318 ol = SYSCTL_CHILDREN(SYSCTL_ADD_NODE(cl, (&SYSCTL_NODE_CHILDREN(_hw, 319 sensors)), sensdev->num, sensdev->xname, CTLFLAG_RD, NULL, "")); 320 SLIST_FOREACH(s, sh, list) { 321 char n[32]; 322 323 ksnprintf(n, sizeof(n), "%s%d", sensor_type_s[s->type], s->numt); 324 SYSCTL_ADD_PROC(cl, ol, OID_AUTO, n, CTLTYPE_STRUCT | 325 CTLFLAG_RD, s, 0, sysctl_handle_sensor, "S,sensor", ""); 326 } 327 } 328 329 void 330 sensor_sysctl8magic_deinstall(struct ksensordev *sensdev) 331 { 332 struct sysctl_ctx_list *cl = &sensdev->clist; 333 334 sysctl_ctx_free(cl); 335 } 336 337 #endif /* !NOSYSCTL8HACK */ 338 339 340 int 341 sysctl_handle_sensordev(SYSCTL_HANDLER_ARGS) 342 { 343 struct ksensordev *ksd = arg1; 344 struct sensordev *usd; 345 int error; 346 347 if (req->newptr) 348 return (EPERM); 349 350 /* Grab a copy, to clear the kernel pointers */ 351 usd = kmalloc(sizeof(*usd), M_TEMP, M_WAITOK | M_ZERO); 352 usd->num = ksd->num; 353 strlcpy(usd->xname, ksd->xname, sizeof(usd->xname)); 354 memcpy(usd->maxnumt, ksd->maxnumt, sizeof(usd->maxnumt)); 355 usd->sensors_count = ksd->sensors_count; 356 357 error = SYSCTL_OUT(req, usd, sizeof(struct sensordev)); 358 359 kfree(usd, M_TEMP); 360 return (error); 361 362 } 363 364 int 365 sysctl_handle_sensor(SYSCTL_HANDLER_ARGS) 366 { 367 struct ksensor *ks = arg1; 368 struct sensor *us; 369 int error; 370 371 if (req->newptr) 372 return (EPERM); 373 374 /* Grab a copy, to clear the kernel pointers */ 375 us = kmalloc(sizeof(*us), M_TEMP, M_WAITOK | M_ZERO); 376 memcpy(us->desc, ks->desc, sizeof(ks->desc)); 377 us->tv = ks->tv; 378 us->value = ks->value; 379 us->type = ks->type; 380 us->status = ks->status; 381 us->numt = ks->numt; 382 us->flags = ks->flags; 383 384 error = SYSCTL_OUT(req, us, sizeof(struct sensor)); 385 386 kfree(us, M_TEMP); 387 return (error); 388 } 389 390 int 391 sysctl_sensors_handler(SYSCTL_HANDLER_ARGS) 392 { 393 int *name = arg1; 394 u_int namelen = arg2; 395 struct ksensordev *ksd; 396 struct ksensor *ks; 397 int dev, numt; 398 enum sensor_type type; 399 400 if (namelen != 1 && namelen != 3) 401 return (ENOTDIR); 402 403 dev = name[0]; 404 if ((ksd = sensordev_get(dev)) == NULL) 405 return (ENOENT); 406 407 if (namelen == 1) 408 return (sysctl_handle_sensordev(NULL, ksd, 0, req)); 409 410 type = name[1]; 411 numt = name[2]; 412 413 if ((ks = sensor_find(ksd, type, numt)) == NULL) 414 return (ENOENT); 415 return (sysctl_handle_sensor(NULL, ks, 0, req)); 416 } 417