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