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 #include <sys/mplock2.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, 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 get_mplock(); 227 228 while (!TAILQ_EMPTY(&tasklist)) { 229 while ((nst = TAILQ_FIRST(&tasklist))->nextrun > 230 (now = time_second)) 231 tsleep(&tasklist, 0, "timeout", 232 (nst->nextrun - now) * hz); 233 234 while ((st = nst) != NULL) { 235 nst = TAILQ_NEXT(st, entry); 236 237 if (st->nextrun > now) 238 break; 239 240 /* take it out while we work on it */ 241 TAILQ_REMOVE(&tasklist, st, entry); 242 243 if (!st->running) { 244 kfree(st, M_DEVBUF); 245 continue; 246 } 247 248 /* run the task */ 249 st->func(st->arg); 250 /* stick it back in the tasklist */ 251 sensor_task_schedule(st); 252 } 253 } 254 255 rel_mplock(); 256 } 257 258 void 259 sensor_task_schedule(struct sensor_task *st) 260 { 261 struct sensor_task *cst; 262 263 st->nextrun = time_second + st->period; 264 265 TAILQ_FOREACH(cst, &tasklist, entry) { 266 if (cst->nextrun > st->nextrun) { 267 TAILQ_INSERT_BEFORE(cst, st, entry); 268 return; 269 } 270 } 271 272 /* must be an empty list, or at the end of the list */ 273 TAILQ_INSERT_TAIL(&tasklist, st, entry); 274 } 275 276 /* 277 * sysctl glue code 278 */ 279 int sysctl_handle_sensordev(SYSCTL_HANDLER_ARGS); 280 int sysctl_handle_sensor(SYSCTL_HANDLER_ARGS); 281 int sysctl_sensors_handler(SYSCTL_HANDLER_ARGS); 282 283 #ifndef NOSYSCTL8HACK 284 285 SYSCTL_NODE(_hw, OID_AUTO, sensors, CTLFLAG_RD, NULL, 286 "Hardware Sensors sysctl internal magic"); 287 SYSCTL_NODE(_hw, HW_SENSORS, _sensors, CTLFLAG_RD, sysctl_sensors_handler, 288 "Hardware Sensors XP MIB interface"); 289 290 #else /* NOSYSCTL8HACK */ 291 292 SYSCTL_NODE(_hw, HW_SENSORS, sensors, CTLFLAG_RD, sysctl_sensors_handler, 293 "Hardware Sensors"); 294 int sensors_debug = 1; 295 SYSCTL_INT(_hw_sensors, OID_AUTO, debug, CTLFLAG_RD, &sensors_debug, 0, "sensors debug"); 296 297 #endif /* !NOSYSCTL8HACK */ 298 299 300 #ifndef NOSYSCTL8HACK 301 302 /* 303 * XXX: 304 * FreeBSD's sysctl(9) .oid_handler functionality is not accustomed 305 * for the CTLTYPE_NODE handler to handle the undocumented sysctl 306 * magic calls. As soon as such functionality is developed, 307 * sysctl_sensors_handler() should be converted to handle all such 308 * calls, and these sysctl_add_oid(9) calls should be removed 309 * "with a big axe". This whole sysctl_add_oid(9) business is solely 310 * to please sysctl(8). 311 */ 312 313 void 314 sensor_sysctl8magic_install(struct ksensordev *sensdev) 315 { 316 struct sysctl_oid_list *ol; 317 struct sysctl_ctx_list *cl = &sensdev->clist; 318 struct ksensor *s; 319 struct ksensors_head *sh = &sensdev->sensors_list; 320 321 sysctl_ctx_init(cl); 322 ol = SYSCTL_CHILDREN(SYSCTL_ADD_NODE(cl, (&SYSCTL_NODE_CHILDREN(_hw, 323 sensors)), sensdev->num, sensdev->xname, CTLFLAG_RD, NULL, "")); 324 SLIST_FOREACH(s, sh, list) { 325 char n[32]; 326 327 ksnprintf(n, sizeof(n), "%s%d", sensor_type_s[s->type], s->numt); 328 SYSCTL_ADD_PROC(cl, ol, OID_AUTO, n, CTLTYPE_STRUCT | 329 CTLFLAG_RD, s, 0, sysctl_handle_sensor, "S,sensor", ""); 330 } 331 } 332 333 void 334 sensor_sysctl8magic_deinstall(struct ksensordev *sensdev) 335 { 336 struct sysctl_ctx_list *cl = &sensdev->clist; 337 338 sysctl_ctx_free(cl); 339 } 340 341 #endif /* !NOSYSCTL8HACK */ 342 343 344 int 345 sysctl_handle_sensordev(SYSCTL_HANDLER_ARGS) 346 { 347 struct ksensordev *ksd = arg1; 348 struct sensordev *usd; 349 int error; 350 351 if (req->newptr) 352 return (EPERM); 353 354 /* Grab a copy, to clear the kernel pointers */ 355 usd = kmalloc(sizeof(*usd), M_TEMP, M_WAITOK | M_ZERO); 356 usd->num = ksd->num; 357 strlcpy(usd->xname, ksd->xname, sizeof(usd->xname)); 358 memcpy(usd->maxnumt, ksd->maxnumt, sizeof(usd->maxnumt)); 359 usd->sensors_count = ksd->sensors_count; 360 361 error = SYSCTL_OUT(req, usd, sizeof(struct sensordev)); 362 363 kfree(usd, M_TEMP); 364 return (error); 365 366 } 367 368 int 369 sysctl_handle_sensor(SYSCTL_HANDLER_ARGS) 370 { 371 struct ksensor *ks = arg1; 372 struct sensor *us; 373 int error; 374 375 if (req->newptr) 376 return (EPERM); 377 378 /* Grab a copy, to clear the kernel pointers */ 379 us = kmalloc(sizeof(*us), M_TEMP, M_WAITOK | M_ZERO); 380 memcpy(us->desc, ks->desc, sizeof(ks->desc)); 381 us->tv = ks->tv; 382 us->value = ks->value; 383 us->type = ks->type; 384 us->status = ks->status; 385 us->numt = ks->numt; 386 us->flags = ks->flags; 387 388 error = SYSCTL_OUT(req, us, sizeof(struct sensor)); 389 390 kfree(us, M_TEMP); 391 return (error); 392 } 393 394 int 395 sysctl_sensors_handler(SYSCTL_HANDLER_ARGS) 396 { 397 int *name = arg1; 398 u_int namelen = arg2; 399 struct ksensordev *ksd; 400 struct ksensor *ks; 401 int dev, numt; 402 enum sensor_type type; 403 404 if (namelen != 1 && namelen != 3) 405 return (ENOTDIR); 406 407 dev = name[0]; 408 if ((ksd = sensordev_get(dev)) == NULL) 409 return (ENOENT); 410 411 if (namelen == 1) 412 return (sysctl_handle_sensordev(NULL, ksd, 0, req)); 413 414 type = name[1]; 415 numt = name[2]; 416 417 if ((ks = sensor_find(ksd, type, numt)) == NULL) 418 return (ENOENT); 419 return (sysctl_handle_sensor(NULL, ks, 0, req)); 420 } 421