kern_et.c revision 210290
1209371Smav/*- 2209371Smav * Copyright (c) 2010 Alexander Motin <mav@FreeBSD.org> 3209371Smav * All rights reserved. 4209371Smav * 5209371Smav * Redistribution and use in source and binary forms, with or without 6209371Smav * modification, are permitted provided that the following conditions 7209371Smav * are met: 8209371Smav * 1. Redistributions of source code must retain the above copyright 9209371Smav * notice, this list of conditions and the following disclaimer, 10209371Smav * without modification, immediately at the beginning of the file. 11209371Smav * 2. Redistributions in binary form must reproduce the above copyright 12209371Smav * notice, this list of conditions and the following disclaimer in the 13209371Smav * documentation and/or other materials provided with the distribution. 14209371Smav * 15209371Smav * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16209371Smav * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17209371Smav * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18209371Smav * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19209371Smav * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20209371Smav * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21209371Smav * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22209371Smav * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23209371Smav * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24209371Smav * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25209371Smav */ 26209371Smav 27209371Smav#include <sys/cdefs.h> 28209371Smav__FBSDID("$FreeBSD: head/sys/kern/kern_et.c 210290 2010-07-20 10:58:56Z mav $"); 29209371Smav 30209371Smav#include <sys/param.h> 31209371Smav#include <sys/kernel.h> 32209371Smav#include <sys/sysctl.h> 33209371Smav#include <sys/systm.h> 34209371Smav#include <sys/queue.h> 35209371Smav#include <sys/timeet.h> 36209371Smav 37209371SmavSLIST_HEAD(et_eventtimers_list, eventtimer); 38209371Smavstatic struct et_eventtimers_list eventtimers = SLIST_HEAD_INITIALIZER(et_eventtimers); 39209371Smav 40209371Smavstruct mtx et_eventtimers_mtx; 41209371SmavMTX_SYSINIT(et_eventtimers_init, &et_eventtimers_mtx, "et_mtx", MTX_SPIN); 42209371Smav 43209371SmavSYSCTL_NODE(_kern, OID_AUTO, eventtimer, CTLFLAG_RW, 0, "Event timers"); 44209371SmavSYSCTL_NODE(_kern_eventtimer, OID_AUTO, et, CTLFLAG_RW, 0, ""); 45209371Smav 46209371Smav/* 47209371Smav * Register a new event timer hardware. 48209371Smav */ 49209371Smavint 50209371Smavet_register(struct eventtimer *et) 51209371Smav{ 52209371Smav struct eventtimer *tmp, *next; 53209371Smav 54209371Smav if (et->et_quality >= 0 || bootverbose) { 55209371Smav printf("Event timer \"%s\" frequency %ju Hz quality %d\n", 56209371Smav et->et_name, (uintmax_t)et->et_frequency, 57209371Smav et->et_quality); 58209371Smav } 59209371Smav et->et_sysctl = SYSCTL_ADD_NODE(NULL, 60209371Smav SYSCTL_STATIC_CHILDREN(_kern_eventtimer_et), OID_AUTO, et->et_name, 61209371Smav CTLFLAG_RW, 0, "event timer description"); 62209371Smav SYSCTL_ADD_UINT(NULL, SYSCTL_CHILDREN(et->et_sysctl), OID_AUTO, 63209371Smav "flags", CTLFLAG_RD, &(et->et_flags), 0, 64209371Smav "Event timer capabilities"); 65209371Smav SYSCTL_ADD_INT(NULL, SYSCTL_CHILDREN(et->et_sysctl), OID_AUTO, 66209371Smav "frequency", CTLFLAG_RD, &(et->et_frequency), 0, 67209371Smav "Event timer base frequency"); 68209371Smav SYSCTL_ADD_INT(NULL, SYSCTL_CHILDREN(et->et_sysctl), OID_AUTO, 69209371Smav "quality", CTLFLAG_RD, &(et->et_quality), 0, 70209371Smav "Goodness of event timer"); 71209371Smav ET_LOCK(); 72209371Smav if (SLIST_EMPTY(&eventtimers) || 73209371Smav SLIST_FIRST(&eventtimers)->et_quality < et->et_quality) { 74209371Smav SLIST_INSERT_HEAD(&eventtimers, et, et_all); 75209371Smav } else { 76209371Smav SLIST_FOREACH(tmp, &eventtimers, et_all) { 77209371Smav next = SLIST_NEXT(tmp, et_all); 78209371Smav if (next == NULL || next->et_quality < et->et_quality) { 79209371Smav SLIST_INSERT_AFTER(tmp, et, et_all); 80209371Smav break; 81209371Smav } 82209371Smav } 83209371Smav } 84209371Smav ET_UNLOCK(); 85209371Smav return (0); 86209371Smav} 87209371Smav 88209371Smav/* 89209371Smav * Deregister event timer hardware. 90209371Smav */ 91209371Smavint 92209371Smavet_deregister(struct eventtimer *et) 93209371Smav{ 94209371Smav int err = 0; 95209371Smav 96209371Smav if (et->et_deregister_cb != NULL) { 97209371Smav if ((err = et->et_deregister_cb(et, et->et_arg)) != 0) 98209371Smav return (err); 99209371Smav } 100209371Smav 101209371Smav ET_LOCK(); 102209371Smav SLIST_REMOVE(&eventtimers, et, eventtimer, et_all); 103209371Smav ET_UNLOCK(); 104209371Smav sysctl_remove_oid(et->et_sysctl, 1, 1); 105209371Smav return (0); 106209371Smav} 107209371Smav 108209371Smav/* 109209371Smav * Find free event timer hardware with specified parameters. 110209371Smav */ 111209371Smavstruct eventtimer * 112209371Smavet_find(const char *name, int check, int want) 113209371Smav{ 114209371Smav struct eventtimer *et = NULL; 115209371Smav 116209371Smav SLIST_FOREACH(et, &eventtimers, et_all) { 117209371Smav if (et->et_active) 118209371Smav continue; 119209371Smav if (name != NULL && strcasecmp(et->et_name, name) != 0) 120209371Smav continue; 121209371Smav if (name == NULL && et->et_quality < 0) 122209371Smav continue; 123209371Smav if ((et->et_flags & check) != want) 124209371Smav continue; 125209371Smav break; 126209371Smav } 127209371Smav return (et); 128209371Smav} 129209371Smav 130209371Smav/* 131209371Smav * Initialize event timer hardware. Set callbacks. 132209371Smav */ 133209371Smavint 134209371Smavet_init(struct eventtimer *et, et_event_cb_t *event, 135209371Smav et_deregister_cb_t *deregister, void *arg) 136209371Smav{ 137209371Smav 138209371Smav if (event == NULL) 139209371Smav return (EINVAL); 140209371Smav if (et->et_active) 141209371Smav return (EBUSY); 142209371Smav 143209371Smav et->et_active = 1; 144209371Smav et->et_event_cb = event; 145209371Smav et->et_deregister_cb = deregister; 146209371Smav et->et_arg = arg; 147209371Smav return (0); 148209371Smav} 149209371Smav 150209371Smav/* 151209371Smav * Start event timer hardware. 152209371Smav * first - delay before first tick. 153209371Smav * period - period of subsequent periodic ticks. 154209371Smav */ 155209371Smavint 156209371Smavet_start(struct eventtimer *et, 157209371Smav struct bintime *first, struct bintime *period) 158209371Smav{ 159209371Smav 160209371Smav if (!et->et_active) 161209371Smav return (ENXIO); 162209371Smav if (first == NULL && period == NULL) 163209371Smav return (EINVAL); 164209371Smav if ((et->et_flags & ET_FLAGS_PERIODIC) == 0 && 165209371Smav period != NULL) 166209371Smav return (ENODEV); 167209371Smav if ((et->et_flags & ET_FLAGS_ONESHOT) == 0 && 168209371Smav period == NULL) 169209371Smav return (ENODEV); 170210290Smav if (first != NULL) { 171210290Smav if (first->sec < et->et_min_period.sec || 172210290Smav (first->sec == et->et_min_period.sec && 173210290Smav first->frac < et->et_min_period.frac)) 174210290Smav first = &et->et_min_period; 175210290Smav if (first->sec > et->et_max_period.sec || 176210290Smav (first->sec == et->et_max_period.sec && 177210290Smav first->frac > et->et_max_period.frac)) 178210290Smav first = &et->et_max_period; 179210290Smav } 180210290Smav if (period != NULL) { 181210290Smav if (period->sec < et->et_min_period.sec || 182210290Smav (period->sec == et->et_min_period.sec && 183210290Smav period->frac < et->et_min_period.frac)) 184210290Smav period = &et->et_min_period; 185210290Smav if (period->sec > et->et_max_period.sec || 186210290Smav (period->sec == et->et_max_period.sec && 187210290Smav period->frac > et->et_max_period.frac)) 188210290Smav period = &et->et_max_period; 189210290Smav } 190209371Smav if (et->et_start) 191209371Smav return (et->et_start(et, first, period)); 192209371Smav return (0); 193209371Smav} 194209371Smav 195209371Smav/* Stop event timer hardware. */ 196209371Smavint 197209371Smavet_stop(struct eventtimer *et) 198209371Smav{ 199209371Smav 200209371Smav if (!et->et_active) 201209371Smav return (ENXIO); 202209371Smav if (et->et_stop) 203209371Smav return (et->et_stop(et)); 204209371Smav return (0); 205209371Smav} 206209371Smav 207209371Smav/* Mark event timer hardware as broken. */ 208209371Smavint 209209371Smavet_ban(struct eventtimer *et) 210209371Smav{ 211209371Smav 212209371Smav et->et_flags &= ~(ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT); 213209371Smav return (0); 214209371Smav} 215209371Smav 216209371Smav/* Free event timer hardware. */ 217209371Smavint 218209371Smavet_free(struct eventtimer *et) 219209371Smav{ 220209371Smav 221209371Smav if (!et->et_active) 222209371Smav return (ENXIO); 223209371Smav 224209371Smav et->et_active = 0; 225209371Smav return (0); 226209371Smav} 227209371Smav 228209371Smav/* Report list of supported event timers hardware via sysctl. */ 229209371Smavstatic int 230209371Smavsysctl_kern_eventtimer_choice(SYSCTL_HANDLER_ARGS) 231209371Smav{ 232209371Smav char buf[512], *spc; 233209371Smav struct eventtimer *et; 234209371Smav int error, off; 235209371Smav 236209371Smav spc = ""; 237209371Smav error = 0; 238209371Smav off = 0; 239209371Smav ET_LOCK(); 240209371Smav SLIST_FOREACH(et, &eventtimers, et_all) { 241209371Smav off += snprintf(buf + off, sizeof(buf) - off, "%s%s(%d)", 242209371Smav spc, et->et_name, et->et_quality); 243209371Smav spc = " "; 244209371Smav } 245209371Smav ET_UNLOCK(); 246209371Smav error = SYSCTL_OUT(req, buf, strlen(buf)); 247209371Smav return (error); 248209371Smav} 249209371SmavSYSCTL_PROC(_kern_eventtimer, OID_AUTO, choice, 250209371Smav CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, 251209371Smav 0, 0, sysctl_kern_eventtimer_choice, "A", "Present event timers"); 252209371Smav 253