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