1875b8844SAlexander Motin /*- 2875b8844SAlexander Motin * Copyright (c) 2010 Alexander Motin <mav@FreeBSD.org> 3875b8844SAlexander Motin * All rights reserved. 4875b8844SAlexander Motin * 5875b8844SAlexander Motin * Redistribution and use in source and binary forms, with or without 6875b8844SAlexander Motin * modification, are permitted provided that the following conditions 7875b8844SAlexander Motin * are met: 8875b8844SAlexander Motin * 1. Redistributions of source code must retain the above copyright 9875b8844SAlexander Motin * notice, this list of conditions and the following disclaimer, 10875b8844SAlexander Motin * without modification, immediately at the beginning of the file. 11875b8844SAlexander Motin * 2. Redistributions in binary form must reproduce the above copyright 12875b8844SAlexander Motin * notice, this list of conditions and the following disclaimer in the 13875b8844SAlexander Motin * documentation and/or other materials provided with the distribution. 14875b8844SAlexander Motin * 15875b8844SAlexander Motin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16875b8844SAlexander Motin * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17875b8844SAlexander Motin * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18875b8844SAlexander Motin * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19875b8844SAlexander Motin * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20875b8844SAlexander Motin * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21875b8844SAlexander Motin * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22875b8844SAlexander Motin * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23875b8844SAlexander Motin * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24875b8844SAlexander Motin * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25875b8844SAlexander Motin */ 26875b8844SAlexander Motin 27875b8844SAlexander Motin #include <sys/cdefs.h> 28875b8844SAlexander Motin __FBSDID("$FreeBSD$"); 29875b8844SAlexander Motin 30875b8844SAlexander Motin #include <sys/param.h> 31875b8844SAlexander Motin #include <sys/kernel.h> 32875b8844SAlexander Motin #include <sys/sysctl.h> 33875b8844SAlexander Motin #include <sys/systm.h> 34875b8844SAlexander Motin #include <sys/queue.h> 35875b8844SAlexander Motin #include <sys/timeet.h> 36875b8844SAlexander Motin 37875b8844SAlexander Motin SLIST_HEAD(et_eventtimers_list, eventtimer); 38875b8844SAlexander Motin static struct et_eventtimers_list eventtimers = SLIST_HEAD_INITIALIZER(et_eventtimers); 39875b8844SAlexander Motin 40875b8844SAlexander Motin struct mtx et_eventtimers_mtx; 41a157e425SAlexander Motin MTX_SYSINIT(et_eventtimers_init, &et_eventtimers_mtx, "et_mtx", MTX_DEF); 42875b8844SAlexander Motin 43875b8844SAlexander Motin SYSCTL_NODE(_kern, OID_AUTO, eventtimer, CTLFLAG_RW, 0, "Event timers"); 44875b8844SAlexander Motin SYSCTL_NODE(_kern_eventtimer, OID_AUTO, et, CTLFLAG_RW, 0, ""); 45875b8844SAlexander Motin 46875b8844SAlexander Motin /* 47875b8844SAlexander Motin * Register a new event timer hardware. 48875b8844SAlexander Motin */ 49875b8844SAlexander Motin int 50875b8844SAlexander Motin et_register(struct eventtimer *et) 51875b8844SAlexander Motin { 52875b8844SAlexander Motin struct eventtimer *tmp, *next; 53875b8844SAlexander Motin 54875b8844SAlexander Motin if (et->et_quality >= 0 || bootverbose) { 5590baf564SAlexander Motin if (et->et_frequency == 0) { 5690baf564SAlexander Motin printf("Event timer \"%s\" quality %d\n", 5790baf564SAlexander Motin et->et_name, et->et_quality); 5890baf564SAlexander Motin } else { 5990baf564SAlexander Motin printf("Event timer \"%s\" " 6090baf564SAlexander Motin "frequency %ju Hz quality %d\n", 61875b8844SAlexander Motin et->et_name, (uintmax_t)et->et_frequency, 62875b8844SAlexander Motin et->et_quality); 63875b8844SAlexander Motin } 6490baf564SAlexander Motin } 65875b8844SAlexander Motin et->et_sysctl = SYSCTL_ADD_NODE(NULL, 66875b8844SAlexander Motin SYSCTL_STATIC_CHILDREN(_kern_eventtimer_et), OID_AUTO, et->et_name, 67875b8844SAlexander Motin CTLFLAG_RW, 0, "event timer description"); 68*fbbb13f9SMatthew D Fleming SYSCTL_ADD_INT(NULL, SYSCTL_CHILDREN(et->et_sysctl), OID_AUTO, 69875b8844SAlexander Motin "flags", CTLFLAG_RD, &(et->et_flags), 0, 70875b8844SAlexander Motin "Event timer capabilities"); 71*fbbb13f9SMatthew D Fleming SYSCTL_ADD_UQUAD(NULL, SYSCTL_CHILDREN(et->et_sysctl), OID_AUTO, 72e88f9fb4SAlexander Motin "frequency", CTLFLAG_RD, &(et->et_frequency), 73875b8844SAlexander Motin "Event timer base frequency"); 74875b8844SAlexander Motin SYSCTL_ADD_INT(NULL, SYSCTL_CHILDREN(et->et_sysctl), OID_AUTO, 75875b8844SAlexander Motin "quality", CTLFLAG_RD, &(et->et_quality), 0, 76875b8844SAlexander Motin "Goodness of event timer"); 77875b8844SAlexander Motin ET_LOCK(); 78875b8844SAlexander Motin if (SLIST_EMPTY(&eventtimers) || 79875b8844SAlexander Motin SLIST_FIRST(&eventtimers)->et_quality < et->et_quality) { 80875b8844SAlexander Motin SLIST_INSERT_HEAD(&eventtimers, et, et_all); 81875b8844SAlexander Motin } else { 82875b8844SAlexander Motin SLIST_FOREACH(tmp, &eventtimers, et_all) { 83875b8844SAlexander Motin next = SLIST_NEXT(tmp, et_all); 84875b8844SAlexander Motin if (next == NULL || next->et_quality < et->et_quality) { 85875b8844SAlexander Motin SLIST_INSERT_AFTER(tmp, et, et_all); 86875b8844SAlexander Motin break; 87875b8844SAlexander Motin } 88875b8844SAlexander Motin } 89875b8844SAlexander Motin } 90875b8844SAlexander Motin ET_UNLOCK(); 91875b8844SAlexander Motin return (0); 92875b8844SAlexander Motin } 93875b8844SAlexander Motin 94875b8844SAlexander Motin /* 95875b8844SAlexander Motin * Deregister event timer hardware. 96875b8844SAlexander Motin */ 97875b8844SAlexander Motin int 98875b8844SAlexander Motin et_deregister(struct eventtimer *et) 99875b8844SAlexander Motin { 100875b8844SAlexander Motin int err = 0; 101875b8844SAlexander Motin 102875b8844SAlexander Motin if (et->et_deregister_cb != NULL) { 103875b8844SAlexander Motin if ((err = et->et_deregister_cb(et, et->et_arg)) != 0) 104875b8844SAlexander Motin return (err); 105875b8844SAlexander Motin } 106875b8844SAlexander Motin 107875b8844SAlexander Motin ET_LOCK(); 108875b8844SAlexander Motin SLIST_REMOVE(&eventtimers, et, eventtimer, et_all); 109875b8844SAlexander Motin ET_UNLOCK(); 110875b8844SAlexander Motin sysctl_remove_oid(et->et_sysctl, 1, 1); 111875b8844SAlexander Motin return (0); 112875b8844SAlexander Motin } 113875b8844SAlexander Motin 114875b8844SAlexander Motin /* 115875b8844SAlexander Motin * Find free event timer hardware with specified parameters. 116875b8844SAlexander Motin */ 117875b8844SAlexander Motin struct eventtimer * 118875b8844SAlexander Motin et_find(const char *name, int check, int want) 119875b8844SAlexander Motin { 120875b8844SAlexander Motin struct eventtimer *et = NULL; 121875b8844SAlexander Motin 122875b8844SAlexander Motin SLIST_FOREACH(et, &eventtimers, et_all) { 123875b8844SAlexander Motin if (et->et_active) 124875b8844SAlexander Motin continue; 125875b8844SAlexander Motin if (name != NULL && strcasecmp(et->et_name, name) != 0) 126875b8844SAlexander Motin continue; 127875b8844SAlexander Motin if (name == NULL && et->et_quality < 0) 128875b8844SAlexander Motin continue; 129875b8844SAlexander Motin if ((et->et_flags & check) != want) 130875b8844SAlexander Motin continue; 131875b8844SAlexander Motin break; 132875b8844SAlexander Motin } 133875b8844SAlexander Motin return (et); 134875b8844SAlexander Motin } 135875b8844SAlexander Motin 136875b8844SAlexander Motin /* 137875b8844SAlexander Motin * Initialize event timer hardware. Set callbacks. 138875b8844SAlexander Motin */ 139875b8844SAlexander Motin int 140875b8844SAlexander Motin et_init(struct eventtimer *et, et_event_cb_t *event, 141875b8844SAlexander Motin et_deregister_cb_t *deregister, void *arg) 142875b8844SAlexander Motin { 143875b8844SAlexander Motin 144875b8844SAlexander Motin if (event == NULL) 145875b8844SAlexander Motin return (EINVAL); 146875b8844SAlexander Motin if (et->et_active) 147875b8844SAlexander Motin return (EBUSY); 148875b8844SAlexander Motin 149875b8844SAlexander Motin et->et_active = 1; 150875b8844SAlexander Motin et->et_event_cb = event; 151875b8844SAlexander Motin et->et_deregister_cb = deregister; 152875b8844SAlexander Motin et->et_arg = arg; 153875b8844SAlexander Motin return (0); 154875b8844SAlexander Motin } 155875b8844SAlexander Motin 156875b8844SAlexander Motin /* 157875b8844SAlexander Motin * Start event timer hardware. 158875b8844SAlexander Motin * first - delay before first tick. 159875b8844SAlexander Motin * period - period of subsequent periodic ticks. 160875b8844SAlexander Motin */ 161875b8844SAlexander Motin int 162875b8844SAlexander Motin et_start(struct eventtimer *et, 163875b8844SAlexander Motin struct bintime *first, struct bintime *period) 164875b8844SAlexander Motin { 165875b8844SAlexander Motin 166875b8844SAlexander Motin if (!et->et_active) 167875b8844SAlexander Motin return (ENXIO); 168875b8844SAlexander Motin if (first == NULL && period == NULL) 169875b8844SAlexander Motin return (EINVAL); 170875b8844SAlexander Motin if ((et->et_flags & ET_FLAGS_PERIODIC) == 0 && 171875b8844SAlexander Motin period != NULL) 172875b8844SAlexander Motin return (ENODEV); 173875b8844SAlexander Motin if ((et->et_flags & ET_FLAGS_ONESHOT) == 0 && 174875b8844SAlexander Motin period == NULL) 175875b8844SAlexander Motin return (ENODEV); 17651636352SAlexander Motin if (first != NULL) { 17751636352SAlexander Motin if (first->sec < et->et_min_period.sec || 17851636352SAlexander Motin (first->sec == et->et_min_period.sec && 17951636352SAlexander Motin first->frac < et->et_min_period.frac)) 18051636352SAlexander Motin first = &et->et_min_period; 18151636352SAlexander Motin if (first->sec > et->et_max_period.sec || 18251636352SAlexander Motin (first->sec == et->et_max_period.sec && 18351636352SAlexander Motin first->frac > et->et_max_period.frac)) 18451636352SAlexander Motin first = &et->et_max_period; 18551636352SAlexander Motin } 18651636352SAlexander Motin if (period != NULL) { 18751636352SAlexander Motin if (period->sec < et->et_min_period.sec || 18851636352SAlexander Motin (period->sec == et->et_min_period.sec && 18951636352SAlexander Motin period->frac < et->et_min_period.frac)) 19051636352SAlexander Motin period = &et->et_min_period; 19151636352SAlexander Motin if (period->sec > et->et_max_period.sec || 19251636352SAlexander Motin (period->sec == et->et_max_period.sec && 19351636352SAlexander Motin period->frac > et->et_max_period.frac)) 19451636352SAlexander Motin period = &et->et_max_period; 19551636352SAlexander Motin } 196875b8844SAlexander Motin if (et->et_start) 197875b8844SAlexander Motin return (et->et_start(et, first, period)); 198875b8844SAlexander Motin return (0); 199875b8844SAlexander Motin } 200875b8844SAlexander Motin 201875b8844SAlexander Motin /* Stop event timer hardware. */ 202875b8844SAlexander Motin int 203875b8844SAlexander Motin et_stop(struct eventtimer *et) 204875b8844SAlexander Motin { 205875b8844SAlexander Motin 206875b8844SAlexander Motin if (!et->et_active) 207875b8844SAlexander Motin return (ENXIO); 208875b8844SAlexander Motin if (et->et_stop) 209875b8844SAlexander Motin return (et->et_stop(et)); 210875b8844SAlexander Motin return (0); 211875b8844SAlexander Motin } 212875b8844SAlexander Motin 213875b8844SAlexander Motin /* Mark event timer hardware as broken. */ 214875b8844SAlexander Motin int 215875b8844SAlexander Motin et_ban(struct eventtimer *et) 216875b8844SAlexander Motin { 217875b8844SAlexander Motin 218875b8844SAlexander Motin et->et_flags &= ~(ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT); 219875b8844SAlexander Motin return (0); 220875b8844SAlexander Motin } 221875b8844SAlexander Motin 222875b8844SAlexander Motin /* Free event timer hardware. */ 223875b8844SAlexander Motin int 224875b8844SAlexander Motin et_free(struct eventtimer *et) 225875b8844SAlexander Motin { 226875b8844SAlexander Motin 227875b8844SAlexander Motin if (!et->et_active) 228875b8844SAlexander Motin return (ENXIO); 229875b8844SAlexander Motin 230875b8844SAlexander Motin et->et_active = 0; 231875b8844SAlexander Motin return (0); 232875b8844SAlexander Motin } 233875b8844SAlexander Motin 234875b8844SAlexander Motin /* Report list of supported event timers hardware via sysctl. */ 235875b8844SAlexander Motin static int 236875b8844SAlexander Motin sysctl_kern_eventtimer_choice(SYSCTL_HANDLER_ARGS) 237875b8844SAlexander Motin { 238875b8844SAlexander Motin char buf[512], *spc; 239875b8844SAlexander Motin struct eventtimer *et; 240875b8844SAlexander Motin int error, off; 241875b8844SAlexander Motin 242875b8844SAlexander Motin spc = ""; 243875b8844SAlexander Motin error = 0; 244d89be950SAlexander Motin buf[0] = 0; 245875b8844SAlexander Motin off = 0; 246875b8844SAlexander Motin ET_LOCK(); 247875b8844SAlexander Motin SLIST_FOREACH(et, &eventtimers, et_all) { 248875b8844SAlexander Motin off += snprintf(buf + off, sizeof(buf) - off, "%s%s(%d)", 249875b8844SAlexander Motin spc, et->et_name, et->et_quality); 250875b8844SAlexander Motin spc = " "; 251875b8844SAlexander Motin } 252875b8844SAlexander Motin ET_UNLOCK(); 253875b8844SAlexander Motin error = SYSCTL_OUT(req, buf, strlen(buf)); 254875b8844SAlexander Motin return (error); 255875b8844SAlexander Motin } 256875b8844SAlexander Motin SYSCTL_PROC(_kern_eventtimer, OID_AUTO, choice, 257875b8844SAlexander Motin CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, 258875b8844SAlexander Motin 0, 0, sysctl_kern_eventtimer_choice, "A", "Present event timers"); 259875b8844SAlexander Motin 260