1 /* $OpenBSD: kern_sensors.c,v 1.19 2007/06/04 18:42:05 deraadt Exp $ */ 2 3 /* 4 * (MPSAFE) 5 * 6 * Copyright (c) 2005 David Gwynne <dlg@openbsd.org> 7 * Copyright (c) 2006 Constantine A. Murenin <cnst+openbsd@bugmail.mojo.ru> 8 * 9 * Permission to use, copy, modify, and distribute this software for any 10 * purpose with or without fee is hereby granted, provided that the above 11 * copyright notice and this permission notice appear in all copies. 12 * 13 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 19 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 20 */ 21 22 #include <sys/param.h> 23 #include <sys/systm.h> 24 #include <sys/kernel.h> 25 #include <sys/malloc.h> 26 #include <sys/kthread.h> 27 #include <sys/queue.h> 28 #include <sys/types.h> 29 #include <sys/time.h> 30 #include <sys/spinlock.h> 31 #include <sys/spinlock2.h> 32 #include <sys/lock.h> 33 34 #include <sys/sysctl.h> 35 #include <sys/sensors.h> 36 37 #include <sys/mplock2.h> 38 39 static int sensor_task_lock_inited = 0; 40 static struct lock sensor_task_lock; 41 static struct spinlock sensor_dev_lock = SPINLOCK_INITIALIZER(sensor_dev_lock); 42 43 int sensordev_count = 0; 44 SLIST_HEAD(, ksensordev) sensordev_list = SLIST_HEAD_INITIALIZER(sensordev_list); 45 46 struct ksensordev *sensordev_get(int); 47 struct ksensor *sensor_find(struct ksensordev *, enum sensor_type, int); 48 49 struct sensor_task { 50 void *arg; 51 void (*func)(void *); 52 53 int period; 54 time_t nextrun; 55 volatile int running; 56 TAILQ_ENTRY(sensor_task) entry; 57 }; 58 59 void sensor_task_thread(void *); 60 void sensor_task_schedule(struct sensor_task *); 61 62 TAILQ_HEAD(, sensor_task) tasklist = TAILQ_HEAD_INITIALIZER(tasklist); 63 64 #ifndef NOSYSCTL8HACK 65 void sensor_sysctl8magic_install(struct ksensordev *); 66 void sensor_sysctl8magic_deinstall(struct ksensordev *); 67 #endif 68 69 void 70 sensordev_install(struct ksensordev *sensdev) 71 { 72 struct ksensordev *v, *nv; 73 74 /* mtx_lock(&Giant); */ 75 spin_lock(&sensor_dev_lock); 76 if (sensordev_count == 0) { 77 sensdev->num = 0; 78 SLIST_INSERT_HEAD(&sensordev_list, sensdev, list); 79 } else { 80 for (v = SLIST_FIRST(&sensordev_list); 81 (nv = SLIST_NEXT(v, list)) != NULL; v = nv) 82 if (nv->num - v->num > 1) 83 break; 84 sensdev->num = v->num + 1; 85 SLIST_INSERT_AFTER(v, sensdev, list); 86 } 87 sensordev_count++; 88 /* mtx_unlock(&Giant); */ 89 spin_unlock(&sensor_dev_lock); 90 91 #ifndef NOSYSCTL8HACK 92 sensor_sysctl8magic_install(sensdev); 93 #endif 94 } 95 96 void 97 sensor_attach(struct ksensordev *sensdev, struct ksensor *sens) 98 { 99 struct ksensor *v, *nv; 100 struct ksensors_head *sh; 101 int i; 102 103 /* mtx_lock(&Giant); */ 104 spin_lock(&sensor_dev_lock); 105 sh = &sensdev->sensors_list; 106 if (sensdev->sensors_count == 0) { 107 for (i = 0; i < SENSOR_MAX_TYPES; i++) 108 sensdev->maxnumt[i] = 0; 109 sens->numt = 0; 110 SLIST_INSERT_HEAD(sh, sens, list); 111 } else { 112 for (v = SLIST_FIRST(sh); 113 (nv = SLIST_NEXT(v, list)) != NULL; v = nv) 114 if (v->type == sens->type && (v->type != nv->type || 115 (v->type == nv->type && nv->numt - v->numt > 1))) 116 break; 117 /* sensors of the same type go after each other */ 118 if (v->type == sens->type) 119 sens->numt = v->numt + 1; 120 else 121 sens->numt = 0; 122 SLIST_INSERT_AFTER(v, sens, list); 123 } 124 /* we only increment maxnumt[] if the sensor was added 125 * to the last position of sensors of this type 126 */ 127 if (sensdev->maxnumt[sens->type] == sens->numt) 128 sensdev->maxnumt[sens->type]++; 129 sensdev->sensors_count++; 130 spin_unlock(&sensor_dev_lock); 131 /* mtx_unlock(&Giant); */ 132 } 133 134 void 135 sensordev_deinstall(struct ksensordev *sensdev) 136 { 137 /* mtx_lock(&Giant); */ 138 spin_lock(&sensor_dev_lock); 139 sensordev_count--; 140 SLIST_REMOVE(&sensordev_list, sensdev, ksensordev, list); 141 /* mtx_unlock(&Giant); */ 142 spin_unlock(&sensor_dev_lock); 143 144 #ifndef NOSYSCTL8HACK 145 sensor_sysctl8magic_deinstall(sensdev); 146 #endif 147 } 148 149 void 150 sensor_detach(struct ksensordev *sensdev, struct ksensor *sens) 151 { 152 struct ksensors_head *sh; 153 154 /* mtx_lock(&Giant); */ 155 sh = &sensdev->sensors_list; 156 sensdev->sensors_count--; 157 SLIST_REMOVE(sh, sens, ksensor, list); 158 /* we only decrement maxnumt[] if this is the tail 159 * sensor of this type 160 */ 161 if (sens->numt == sensdev->maxnumt[sens->type] - 1) 162 sensdev->maxnumt[sens->type]--; 163 /* mtx_unlock(&Giant); */ 164 } 165 166 struct ksensordev * 167 sensordev_get(int num) 168 { 169 struct ksensordev *sd; 170 171 spin_lock(&sensor_dev_lock); 172 SLIST_FOREACH(sd, &sensordev_list, list) 173 if (sd->num == num) { 174 spin_unlock(&sensor_dev_lock); 175 return (sd); 176 } 177 178 spin_unlock(&sensor_dev_lock); 179 return (NULL); 180 } 181 182 struct ksensor * 183 sensor_find(struct ksensordev *sensdev, enum sensor_type type, int numt) 184 { 185 struct ksensor *s; 186 struct ksensors_head *sh; 187 188 spin_lock(&sensor_dev_lock); 189 sh = &sensdev->sensors_list; 190 SLIST_FOREACH(s, sh, list) { 191 if (s->type == type && s->numt == numt) { 192 spin_unlock(&sensor_dev_lock); 193 return (s); 194 } 195 } 196 197 spin_unlock(&sensor_dev_lock); 198 return (NULL); 199 } 200 201 int 202 sensor_task_register(void *arg, void (*func)(void *), int period) 203 { 204 struct sensor_task *st; 205 int create_thread = 0; 206 207 st = kmalloc(sizeof(struct sensor_task), M_DEVBUF, M_NOWAIT); 208 if (st == NULL) 209 return (1); 210 211 if (atomic_cmpset_int(&sensor_task_lock_inited, 0, 1)) { 212 lockinit(&sensor_task_lock, "ksensor_task", 0, LK_CANRECURSE); 213 } 214 215 lockmgr(&sensor_task_lock, LK_EXCLUSIVE); 216 st->arg = arg; 217 st->func = func; 218 st->period = period; 219 220 st->running = 1; 221 222 if (TAILQ_EMPTY(&tasklist)) 223 create_thread = 1; 224 225 st->nextrun = 0; 226 TAILQ_INSERT_HEAD(&tasklist, st, entry); 227 228 if (create_thread) 229 if (kthread_create(sensor_task_thread, NULL, NULL, 230 "sensors") != 0) 231 panic("sensors kthread"); 232 233 wakeup(&tasklist); 234 235 lockmgr(&sensor_task_lock, LK_RELEASE); 236 return (0); 237 } 238 239 void 240 sensor_task_unregister(void *arg) 241 { 242 struct sensor_task *st; 243 244 lockmgr(&sensor_task_lock, LK_EXCLUSIVE); 245 TAILQ_FOREACH(st, &tasklist, entry) 246 if (st->arg == arg) 247 st->running = 0; 248 lockmgr(&sensor_task_lock, LK_RELEASE); 249 } 250 251 void 252 sensor_task_thread(void *arg) 253 { 254 struct sensor_task *st, *nst; 255 time_t now; 256 257 lockmgr(&sensor_task_lock, LK_EXCLUSIVE); 258 259 while (!TAILQ_EMPTY(&tasklist)) { 260 while ((nst = TAILQ_FIRST(&tasklist))->nextrun > 261 (now = time_second)) 262 lksleep(&tasklist, &sensor_task_lock, 0, "timeout", 263 (nst->nextrun - now) * hz); 264 265 while ((st = nst) != NULL) { 266 nst = TAILQ_NEXT(st, entry); 267 268 if (st->nextrun > now) 269 break; 270 271 /* take it out while we work on it */ 272 TAILQ_REMOVE(&tasklist, st, entry); 273 274 if (!st->running) { 275 kfree(st, M_DEVBUF); 276 continue; 277 } 278 279 /* run the task */ 280 st->func(st->arg); 281 /* stick it back in the tasklist */ 282 sensor_task_schedule(st); 283 } 284 } 285 286 lockmgr(&sensor_task_lock, LK_RELEASE); 287 } 288 289 void 290 sensor_task_schedule(struct sensor_task *st) 291 { 292 struct sensor_task *cst; 293 294 lockmgr(&sensor_task_lock, LK_EXCLUSIVE); 295 st->nextrun = time_second + st->period; 296 297 TAILQ_FOREACH(cst, &tasklist, entry) { 298 if (cst->nextrun > st->nextrun) { 299 TAILQ_INSERT_BEFORE(cst, st, entry); 300 lockmgr(&sensor_task_lock, LK_RELEASE); 301 return; 302 } 303 } 304 305 /* must be an empty list, or at the end of the list */ 306 TAILQ_INSERT_TAIL(&tasklist, st, entry); 307 lockmgr(&sensor_task_lock, LK_RELEASE); 308 } 309 310 /* 311 * sysctl glue code 312 */ 313 int sysctl_handle_sensordev(SYSCTL_HANDLER_ARGS); 314 int sysctl_handle_sensor(SYSCTL_HANDLER_ARGS); 315 int sysctl_sensors_handler(SYSCTL_HANDLER_ARGS); 316 317 #ifndef NOSYSCTL8HACK 318 319 SYSCTL_NODE(_hw, OID_AUTO, sensors, CTLFLAG_RD, NULL, 320 "Hardware Sensors sysctl internal magic"); 321 SYSCTL_NODE(_hw, HW_SENSORS, _sensors, CTLFLAG_RD, sysctl_sensors_handler, 322 "Hardware Sensors XP MIB interface"); 323 324 #else /* NOSYSCTL8HACK */ 325 326 SYSCTL_NODE(_hw, HW_SENSORS, sensors, CTLFLAG_RD, sysctl_sensors_handler, 327 "Hardware Sensors"); 328 int sensors_debug = 1; 329 SYSCTL_INT(_hw_sensors, OID_AUTO, debug, CTLFLAG_RD, &sensors_debug, 0, "sensors debug"); 330 331 #endif /* !NOSYSCTL8HACK */ 332 333 334 #ifndef NOSYSCTL8HACK 335 336 /* 337 * XXX: 338 * FreeBSD's sysctl(9) .oid_handler functionality is not accustomed 339 * for the CTLTYPE_NODE handler to handle the undocumented sysctl 340 * magic calls. As soon as such functionality is developed, 341 * sysctl_sensors_handler() should be converted to handle all such 342 * calls, and these sysctl_add_oid(9) calls should be removed 343 * "with a big axe". This whole sysctl_add_oid(9) business is solely 344 * to please sysctl(8). 345 */ 346 347 void 348 sensor_sysctl8magic_install(struct ksensordev *sensdev) 349 { 350 struct sysctl_oid_list *ol; 351 struct sysctl_ctx_list *cl = &sensdev->clist; 352 struct ksensor *s; 353 struct ksensors_head *sh = &sensdev->sensors_list; 354 355 sysctl_ctx_init(cl); 356 ol = SYSCTL_CHILDREN(SYSCTL_ADD_NODE(cl, (&SYSCTL_NODE_CHILDREN(_hw, 357 sensors)), sensdev->num, sensdev->xname, CTLFLAG_RD, NULL, "")); 358 SLIST_FOREACH(s, sh, list) { 359 char n[32]; 360 361 ksnprintf(n, sizeof(n), "%s%d", sensor_type_s[s->type], s->numt); 362 SYSCTL_ADD_PROC(cl, ol, OID_AUTO, n, CTLTYPE_STRUCT | 363 CTLFLAG_RD, s, 0, sysctl_handle_sensor, "S,sensor", ""); 364 } 365 } 366 367 void 368 sensor_sysctl8magic_deinstall(struct ksensordev *sensdev) 369 { 370 struct sysctl_ctx_list *cl = &sensdev->clist; 371 372 sysctl_ctx_free(cl); 373 } 374 375 #endif /* !NOSYSCTL8HACK */ 376 377 378 int 379 sysctl_handle_sensordev(SYSCTL_HANDLER_ARGS) 380 { 381 struct ksensordev *ksd = arg1; 382 struct sensordev *usd; 383 int error; 384 385 if (req->newptr) 386 return (EPERM); 387 388 /* Grab a copy, to clear the kernel pointers */ 389 usd = kmalloc(sizeof(*usd), M_TEMP, M_WAITOK | M_ZERO); 390 usd->num = ksd->num; 391 strlcpy(usd->xname, ksd->xname, sizeof(usd->xname)); 392 memcpy(usd->maxnumt, ksd->maxnumt, sizeof(usd->maxnumt)); 393 usd->sensors_count = ksd->sensors_count; 394 395 error = SYSCTL_OUT(req, usd, sizeof(struct sensordev)); 396 397 kfree(usd, M_TEMP); 398 return (error); 399 400 } 401 402 int 403 sysctl_handle_sensor(SYSCTL_HANDLER_ARGS) 404 { 405 struct ksensor *ks = arg1; 406 struct sensor *us; 407 int error; 408 409 if (req->newptr) 410 return (EPERM); 411 412 /* Grab a copy, to clear the kernel pointers */ 413 us = kmalloc(sizeof(*us), M_TEMP, M_WAITOK | M_ZERO); 414 memcpy(us->desc, ks->desc, sizeof(ks->desc)); 415 us->tv = ks->tv; 416 us->value = ks->value; 417 us->type = ks->type; 418 us->status = ks->status; 419 us->numt = ks->numt; 420 us->flags = ks->flags; 421 422 error = SYSCTL_OUT(req, us, sizeof(struct sensor)); 423 424 kfree(us, M_TEMP); 425 return (error); 426 } 427 428 int 429 sysctl_sensors_handler(SYSCTL_HANDLER_ARGS) 430 { 431 int *name = arg1; 432 u_int namelen = arg2; 433 struct ksensordev *ksd; 434 struct ksensor *ks; 435 int dev, numt; 436 enum sensor_type type; 437 438 if (namelen != 1 && namelen != 3) 439 return (ENOTDIR); 440 441 dev = name[0]; 442 if ((ksd = sensordev_get(dev)) == NULL) 443 return (ENOENT); 444 445 if (namelen == 1) 446 return (sysctl_handle_sensordev(NULL, ksd, 0, req)); 447 448 type = name[1]; 449 numt = name[2]; 450 451 if ((ks = sensor_find(ksd, type, numt)) == NULL) 452 return (ENOENT); 453 return (sysctl_handle_sensor(NULL, ks, 0, req)); 454 } 455