1209371Smav/*- 2247463Smav * Copyright (c) 2010-2013 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$"); 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; 41212541SmavMTX_SYSINIT(et_eventtimers_init, &et_eventtimers_mtx, "et_mtx", MTX_DEF); 42209371Smav 43209371SmavSYSCTL_NODE(_kern, OID_AUTO, eventtimer, CTLFLAG_RW, 0, "Event timers"); 44227309Sedstatic SYSCTL_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) { 55212479Smav if (et->et_frequency == 0) { 56212479Smav printf("Event timer \"%s\" quality %d\n", 57212479Smav et->et_name, et->et_quality); 58212479Smav } else { 59212479Smav printf("Event timer \"%s\" " 60212479Smav "frequency %ju Hz quality %d\n", 61212479Smav et->et_name, (uintmax_t)et->et_frequency, 62212479Smav et->et_quality); 63212479Smav } 64209371Smav } 65247463Smav KASSERT(et->et_start, ("et_register: timer has no start function")); 66209371Smav et->et_sysctl = SYSCTL_ADD_NODE(NULL, 67209371Smav SYSCTL_STATIC_CHILDREN(_kern_eventtimer_et), OID_AUTO, et->et_name, 68209371Smav CTLFLAG_RW, 0, "event timer description"); 69217326Smdf SYSCTL_ADD_INT(NULL, SYSCTL_CHILDREN(et->et_sysctl), OID_AUTO, 70209371Smav "flags", CTLFLAG_RD, &(et->et_flags), 0, 71209371Smav "Event timer capabilities"); 72217326Smdf SYSCTL_ADD_UQUAD(NULL, SYSCTL_CHILDREN(et->et_sysctl), OID_AUTO, 73210346Smav "frequency", CTLFLAG_RD, &(et->et_frequency), 74209371Smav "Event timer base frequency"); 75209371Smav SYSCTL_ADD_INT(NULL, SYSCTL_CHILDREN(et->et_sysctl), OID_AUTO, 76209371Smav "quality", CTLFLAG_RD, &(et->et_quality), 0, 77209371Smav "Goodness of event timer"); 78209371Smav ET_LOCK(); 79209371Smav if (SLIST_EMPTY(&eventtimers) || 80209371Smav SLIST_FIRST(&eventtimers)->et_quality < et->et_quality) { 81209371Smav SLIST_INSERT_HEAD(&eventtimers, et, et_all); 82209371Smav } else { 83209371Smav SLIST_FOREACH(tmp, &eventtimers, et_all) { 84209371Smav next = SLIST_NEXT(tmp, et_all); 85209371Smav if (next == NULL || next->et_quality < et->et_quality) { 86209371Smav SLIST_INSERT_AFTER(tmp, et, et_all); 87209371Smav break; 88209371Smav } 89209371Smav } 90209371Smav } 91209371Smav ET_UNLOCK(); 92209371Smav return (0); 93209371Smav} 94209371Smav 95209371Smav/* 96209371Smav * Deregister event timer hardware. 97209371Smav */ 98209371Smavint 99209371Smavet_deregister(struct eventtimer *et) 100209371Smav{ 101209371Smav int err = 0; 102209371Smav 103209371Smav if (et->et_deregister_cb != NULL) { 104209371Smav if ((err = et->et_deregister_cb(et, et->et_arg)) != 0) 105209371Smav return (err); 106209371Smav } 107209371Smav 108209371Smav ET_LOCK(); 109209371Smav SLIST_REMOVE(&eventtimers, et, eventtimer, et_all); 110209371Smav ET_UNLOCK(); 111209371Smav sysctl_remove_oid(et->et_sysctl, 1, 1); 112209371Smav return (0); 113209371Smav} 114209371Smav 115209371Smav/* 116209371Smav * Find free event timer hardware with specified parameters. 117209371Smav */ 118209371Smavstruct eventtimer * 119209371Smavet_find(const char *name, int check, int want) 120209371Smav{ 121209371Smav struct eventtimer *et = NULL; 122209371Smav 123209371Smav SLIST_FOREACH(et, &eventtimers, et_all) { 124209371Smav if (et->et_active) 125209371Smav continue; 126209371Smav if (name != NULL && strcasecmp(et->et_name, name) != 0) 127209371Smav continue; 128209371Smav if (name == NULL && et->et_quality < 0) 129209371Smav continue; 130209371Smav if ((et->et_flags & check) != want) 131209371Smav continue; 132209371Smav break; 133209371Smav } 134209371Smav return (et); 135209371Smav} 136209371Smav 137209371Smav/* 138209371Smav * Initialize event timer hardware. Set callbacks. 139209371Smav */ 140209371Smavint 141209371Smavet_init(struct eventtimer *et, et_event_cb_t *event, 142209371Smav et_deregister_cb_t *deregister, void *arg) 143209371Smav{ 144209371Smav 145209371Smav if (event == NULL) 146209371Smav return (EINVAL); 147209371Smav if (et->et_active) 148209371Smav return (EBUSY); 149209371Smav 150209371Smav et->et_active = 1; 151209371Smav et->et_event_cb = event; 152209371Smav et->et_deregister_cb = deregister; 153209371Smav et->et_arg = arg; 154209371Smav return (0); 155209371Smav} 156209371Smav 157209371Smav/* 158209371Smav * Start event timer hardware. 159209371Smav * first - delay before first tick. 160209371Smav * period - period of subsequent periodic ticks. 161209371Smav */ 162209371Smavint 163247463Smavet_start(struct eventtimer *et, sbintime_t first, sbintime_t period) 164209371Smav{ 165209371Smav 166209371Smav if (!et->et_active) 167209371Smav return (ENXIO); 168247463Smav KASSERT(period >= 0, ("et_start: negative period")); 169247463Smav KASSERT((et->et_flags & ET_FLAGS_PERIODIC) || period == 0, 170247463Smav ("et_start: period specified for oneshot-only timer")); 171248230Smav KASSERT((et->et_flags & ET_FLAGS_ONESHOT) || period != 0, 172247463Smav ("et_start: period not specified for periodic-only timer")); 173247463Smav if (period != 0) { 174247463Smav if (period < et->et_min_period) 175247463Smav period = et->et_min_period; 176247463Smav else if (period > et->et_max_period) 177247463Smav period = et->et_max_period; 178210290Smav } 179247463Smav if (period == 0 || first != 0) { 180247463Smav if (first < et->et_min_period) 181247463Smav first = et->et_min_period; 182247463Smav else if (first > et->et_max_period) 183247463Smav first = et->et_max_period; 184210290Smav } 185247463Smav return (et->et_start(et, first, period)); 186209371Smav} 187209371Smav 188209371Smav/* Stop event timer hardware. */ 189209371Smavint 190209371Smavet_stop(struct eventtimer *et) 191209371Smav{ 192209371Smav 193209371Smav if (!et->et_active) 194209371Smav return (ENXIO); 195209371Smav if (et->et_stop) 196209371Smav return (et->et_stop(et)); 197209371Smav return (0); 198209371Smav} 199209371Smav 200209371Smav/* Mark event timer hardware as broken. */ 201209371Smavint 202209371Smavet_ban(struct eventtimer *et) 203209371Smav{ 204209371Smav 205209371Smav et->et_flags &= ~(ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT); 206209371Smav return (0); 207209371Smav} 208209371Smav 209209371Smav/* Free event timer hardware. */ 210209371Smavint 211209371Smavet_free(struct eventtimer *et) 212209371Smav{ 213209371Smav 214209371Smav if (!et->et_active) 215209371Smav return (ENXIO); 216209371Smav 217209371Smav et->et_active = 0; 218209371Smav return (0); 219209371Smav} 220209371Smav 221209371Smav/* Report list of supported event timers hardware via sysctl. */ 222209371Smavstatic int 223209371Smavsysctl_kern_eventtimer_choice(SYSCTL_HANDLER_ARGS) 224209371Smav{ 225209371Smav char buf[512], *spc; 226209371Smav struct eventtimer *et; 227209371Smav int error, off; 228209371Smav 229209371Smav spc = ""; 230209371Smav error = 0; 231212223Smav buf[0] = 0; 232209371Smav off = 0; 233209371Smav ET_LOCK(); 234209371Smav SLIST_FOREACH(et, &eventtimers, et_all) { 235209371Smav off += snprintf(buf + off, sizeof(buf) - off, "%s%s(%d)", 236209371Smav spc, et->et_name, et->et_quality); 237209371Smav spc = " "; 238209371Smav } 239209371Smav ET_UNLOCK(); 240209371Smav error = SYSCTL_OUT(req, buf, strlen(buf)); 241209371Smav return (error); 242209371Smav} 243209371SmavSYSCTL_PROC(_kern_eventtimer, OID_AUTO, choice, 244209371Smav CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, 245209371Smav 0, 0, sysctl_kern_eventtimer_choice, "A", "Present event timers"); 246209371Smav 247