1875b8844SAlexander Motin /*- 2*fdc5dd2dSAlexander Motin * Copyright (c) 2010-2013 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"); 446472ac3dSEd Schouten static 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 } 65*fdc5dd2dSAlexander Motin KASSERT(et->et_start, ("et_register: timer has no start function")); 66875b8844SAlexander Motin et->et_sysctl = SYSCTL_ADD_NODE(NULL, 67875b8844SAlexander Motin SYSCTL_STATIC_CHILDREN(_kern_eventtimer_et), OID_AUTO, et->et_name, 68875b8844SAlexander Motin CTLFLAG_RW, 0, "event timer description"); 69fbbb13f9SMatthew D Fleming SYSCTL_ADD_INT(NULL, SYSCTL_CHILDREN(et->et_sysctl), OID_AUTO, 70875b8844SAlexander Motin "flags", CTLFLAG_RD, &(et->et_flags), 0, 71875b8844SAlexander Motin "Event timer capabilities"); 72fbbb13f9SMatthew D Fleming SYSCTL_ADD_UQUAD(NULL, SYSCTL_CHILDREN(et->et_sysctl), OID_AUTO, 73e88f9fb4SAlexander Motin "frequency", CTLFLAG_RD, &(et->et_frequency), 74875b8844SAlexander Motin "Event timer base frequency"); 75875b8844SAlexander Motin SYSCTL_ADD_INT(NULL, SYSCTL_CHILDREN(et->et_sysctl), OID_AUTO, 76875b8844SAlexander Motin "quality", CTLFLAG_RD, &(et->et_quality), 0, 77875b8844SAlexander Motin "Goodness of event timer"); 78875b8844SAlexander Motin ET_LOCK(); 79875b8844SAlexander Motin if (SLIST_EMPTY(&eventtimers) || 80875b8844SAlexander Motin SLIST_FIRST(&eventtimers)->et_quality < et->et_quality) { 81875b8844SAlexander Motin SLIST_INSERT_HEAD(&eventtimers, et, et_all); 82875b8844SAlexander Motin } else { 83875b8844SAlexander Motin SLIST_FOREACH(tmp, &eventtimers, et_all) { 84875b8844SAlexander Motin next = SLIST_NEXT(tmp, et_all); 85875b8844SAlexander Motin if (next == NULL || next->et_quality < et->et_quality) { 86875b8844SAlexander Motin SLIST_INSERT_AFTER(tmp, et, et_all); 87875b8844SAlexander Motin break; 88875b8844SAlexander Motin } 89875b8844SAlexander Motin } 90875b8844SAlexander Motin } 91875b8844SAlexander Motin ET_UNLOCK(); 92875b8844SAlexander Motin return (0); 93875b8844SAlexander Motin } 94875b8844SAlexander Motin 95875b8844SAlexander Motin /* 96875b8844SAlexander Motin * Deregister event timer hardware. 97875b8844SAlexander Motin */ 98875b8844SAlexander Motin int 99875b8844SAlexander Motin et_deregister(struct eventtimer *et) 100875b8844SAlexander Motin { 101875b8844SAlexander Motin int err = 0; 102875b8844SAlexander Motin 103875b8844SAlexander Motin if (et->et_deregister_cb != NULL) { 104875b8844SAlexander Motin if ((err = et->et_deregister_cb(et, et->et_arg)) != 0) 105875b8844SAlexander Motin return (err); 106875b8844SAlexander Motin } 107875b8844SAlexander Motin 108875b8844SAlexander Motin ET_LOCK(); 109875b8844SAlexander Motin SLIST_REMOVE(&eventtimers, et, eventtimer, et_all); 110875b8844SAlexander Motin ET_UNLOCK(); 111875b8844SAlexander Motin sysctl_remove_oid(et->et_sysctl, 1, 1); 112875b8844SAlexander Motin return (0); 113875b8844SAlexander Motin } 114875b8844SAlexander Motin 115875b8844SAlexander Motin /* 116875b8844SAlexander Motin * Find free event timer hardware with specified parameters. 117875b8844SAlexander Motin */ 118875b8844SAlexander Motin struct eventtimer * 119875b8844SAlexander Motin et_find(const char *name, int check, int want) 120875b8844SAlexander Motin { 121875b8844SAlexander Motin struct eventtimer *et = NULL; 122875b8844SAlexander Motin 123875b8844SAlexander Motin SLIST_FOREACH(et, &eventtimers, et_all) { 124875b8844SAlexander Motin if (et->et_active) 125875b8844SAlexander Motin continue; 126875b8844SAlexander Motin if (name != NULL && strcasecmp(et->et_name, name) != 0) 127875b8844SAlexander Motin continue; 128875b8844SAlexander Motin if (name == NULL && et->et_quality < 0) 129875b8844SAlexander Motin continue; 130875b8844SAlexander Motin if ((et->et_flags & check) != want) 131875b8844SAlexander Motin continue; 132875b8844SAlexander Motin break; 133875b8844SAlexander Motin } 134875b8844SAlexander Motin return (et); 135875b8844SAlexander Motin } 136875b8844SAlexander Motin 137875b8844SAlexander Motin /* 138875b8844SAlexander Motin * Initialize event timer hardware. Set callbacks. 139875b8844SAlexander Motin */ 140875b8844SAlexander Motin int 141875b8844SAlexander Motin et_init(struct eventtimer *et, et_event_cb_t *event, 142875b8844SAlexander Motin et_deregister_cb_t *deregister, void *arg) 143875b8844SAlexander Motin { 144875b8844SAlexander Motin 145875b8844SAlexander Motin if (event == NULL) 146875b8844SAlexander Motin return (EINVAL); 147875b8844SAlexander Motin if (et->et_active) 148875b8844SAlexander Motin return (EBUSY); 149875b8844SAlexander Motin 150875b8844SAlexander Motin et->et_active = 1; 151875b8844SAlexander Motin et->et_event_cb = event; 152875b8844SAlexander Motin et->et_deregister_cb = deregister; 153875b8844SAlexander Motin et->et_arg = arg; 154875b8844SAlexander Motin return (0); 155875b8844SAlexander Motin } 156875b8844SAlexander Motin 157875b8844SAlexander Motin /* 158875b8844SAlexander Motin * Start event timer hardware. 159875b8844SAlexander Motin * first - delay before first tick. 160875b8844SAlexander Motin * period - period of subsequent periodic ticks. 161875b8844SAlexander Motin */ 162875b8844SAlexander Motin int 163*fdc5dd2dSAlexander Motin et_start(struct eventtimer *et, sbintime_t first, sbintime_t period) 164875b8844SAlexander Motin { 165875b8844SAlexander Motin 166875b8844SAlexander Motin if (!et->et_active) 167875b8844SAlexander Motin return (ENXIO); 168*fdc5dd2dSAlexander Motin KASSERT(period >= 0, ("et_start: negative period")); 169*fdc5dd2dSAlexander Motin KASSERT((et->et_flags & ET_FLAGS_PERIODIC) || period == 0, 170*fdc5dd2dSAlexander Motin ("et_start: period specified for oneshot-only timer")); 171*fdc5dd2dSAlexander Motin KASSERT((et->et_flags & ET_FLAGS_ONESHOT) && period == 0, 172*fdc5dd2dSAlexander Motin ("et_start: period not specified for periodic-only timer")); 173*fdc5dd2dSAlexander Motin if (period != 0) { 174*fdc5dd2dSAlexander Motin if (period < et->et_min_period) 175*fdc5dd2dSAlexander Motin period = et->et_min_period; 176*fdc5dd2dSAlexander Motin else if (period > et->et_max_period) 177*fdc5dd2dSAlexander Motin period = et->et_max_period; 17851636352SAlexander Motin } 179*fdc5dd2dSAlexander Motin if (period == 0 || first != 0) { 180*fdc5dd2dSAlexander Motin if (first < et->et_min_period) 181*fdc5dd2dSAlexander Motin first = et->et_min_period; 182*fdc5dd2dSAlexander Motin else if (first > et->et_max_period) 183*fdc5dd2dSAlexander Motin first = et->et_max_period; 18451636352SAlexander Motin } 185875b8844SAlexander Motin return (et->et_start(et, first, period)); 186875b8844SAlexander Motin } 187875b8844SAlexander Motin 188875b8844SAlexander Motin /* Stop event timer hardware. */ 189875b8844SAlexander Motin int 190875b8844SAlexander Motin et_stop(struct eventtimer *et) 191875b8844SAlexander Motin { 192875b8844SAlexander Motin 193875b8844SAlexander Motin if (!et->et_active) 194875b8844SAlexander Motin return (ENXIO); 195875b8844SAlexander Motin if (et->et_stop) 196875b8844SAlexander Motin return (et->et_stop(et)); 197875b8844SAlexander Motin return (0); 198875b8844SAlexander Motin } 199875b8844SAlexander Motin 200875b8844SAlexander Motin /* Mark event timer hardware as broken. */ 201875b8844SAlexander Motin int 202875b8844SAlexander Motin et_ban(struct eventtimer *et) 203875b8844SAlexander Motin { 204875b8844SAlexander Motin 205875b8844SAlexander Motin et->et_flags &= ~(ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT); 206875b8844SAlexander Motin return (0); 207875b8844SAlexander Motin } 208875b8844SAlexander Motin 209875b8844SAlexander Motin /* Free event timer hardware. */ 210875b8844SAlexander Motin int 211875b8844SAlexander Motin et_free(struct eventtimer *et) 212875b8844SAlexander Motin { 213875b8844SAlexander Motin 214875b8844SAlexander Motin if (!et->et_active) 215875b8844SAlexander Motin return (ENXIO); 216875b8844SAlexander Motin 217875b8844SAlexander Motin et->et_active = 0; 218875b8844SAlexander Motin return (0); 219875b8844SAlexander Motin } 220875b8844SAlexander Motin 221875b8844SAlexander Motin /* Report list of supported event timers hardware via sysctl. */ 222875b8844SAlexander Motin static int 223875b8844SAlexander Motin sysctl_kern_eventtimer_choice(SYSCTL_HANDLER_ARGS) 224875b8844SAlexander Motin { 225875b8844SAlexander Motin char buf[512], *spc; 226875b8844SAlexander Motin struct eventtimer *et; 227875b8844SAlexander Motin int error, off; 228875b8844SAlexander Motin 229875b8844SAlexander Motin spc = ""; 230875b8844SAlexander Motin error = 0; 231d89be950SAlexander Motin buf[0] = 0; 232875b8844SAlexander Motin off = 0; 233875b8844SAlexander Motin ET_LOCK(); 234875b8844SAlexander Motin SLIST_FOREACH(et, &eventtimers, et_all) { 235875b8844SAlexander Motin off += snprintf(buf + off, sizeof(buf) - off, "%s%s(%d)", 236875b8844SAlexander Motin spc, et->et_name, et->et_quality); 237875b8844SAlexander Motin spc = " "; 238875b8844SAlexander Motin } 239875b8844SAlexander Motin ET_UNLOCK(); 240875b8844SAlexander Motin error = SYSCTL_OUT(req, buf, strlen(buf)); 241875b8844SAlexander Motin return (error); 242875b8844SAlexander Motin } 243875b8844SAlexander Motin SYSCTL_PROC(_kern_eventtimer, OID_AUTO, choice, 244875b8844SAlexander Motin CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, 245875b8844SAlexander Motin 0, 0, sysctl_kern_eventtimer_choice, "A", "Present event timers"); 246875b8844SAlexander Motin 247