]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - sys/kern/kern_et.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / sys / kern / kern_et.c
1 /*-
2  * Copyright (c) 2010 Alexander Motin <mav@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer,
10  *    without modification, immediately at the beginning of the file.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include <sys/param.h>
31 #include <sys/kernel.h>
32 #include <sys/sysctl.h>
33 #include <sys/systm.h>
34 #include <sys/queue.h>
35 #include <sys/timeet.h>
36
37 SLIST_HEAD(et_eventtimers_list, eventtimer);
38 static struct et_eventtimers_list eventtimers = SLIST_HEAD_INITIALIZER(et_eventtimers);
39
40 struct mtx      et_eventtimers_mtx;
41 MTX_SYSINIT(et_eventtimers_init, &et_eventtimers_mtx, "et_mtx", MTX_DEF);
42
43 SYSCTL_NODE(_kern, OID_AUTO, eventtimer, CTLFLAG_RW, 0, "Event timers");
44 static SYSCTL_NODE(_kern_eventtimer, OID_AUTO, et, CTLFLAG_RW, 0, "");
45
46 /*
47  * Register a new event timer hardware.
48  */
49 int
50 et_register(struct eventtimer *et)
51 {
52         struct eventtimer *tmp, *next;
53
54         if (et->et_quality >= 0 || bootverbose) {
55                 if (et->et_frequency == 0) {
56                         printf("Event timer \"%s\" quality %d\n",
57                             et->et_name, et->et_quality);
58                 } else {
59                         printf("Event timer \"%s\" "
60                             "frequency %ju Hz quality %d\n",
61                             et->et_name, (uintmax_t)et->et_frequency,
62                             et->et_quality);
63                 }
64         }
65         et->et_sysctl = SYSCTL_ADD_NODE(NULL,
66             SYSCTL_STATIC_CHILDREN(_kern_eventtimer_et), OID_AUTO, et->et_name,
67             CTLFLAG_RW, 0, "event timer description");
68         SYSCTL_ADD_INT(NULL, SYSCTL_CHILDREN(et->et_sysctl), OID_AUTO,
69             "flags", CTLFLAG_RD, &(et->et_flags), 0,
70             "Event timer capabilities");
71         SYSCTL_ADD_UQUAD(NULL, SYSCTL_CHILDREN(et->et_sysctl), OID_AUTO,
72             "frequency", CTLFLAG_RD, &(et->et_frequency),
73             "Event timer base frequency");
74         SYSCTL_ADD_INT(NULL, SYSCTL_CHILDREN(et->et_sysctl), OID_AUTO,
75             "quality", CTLFLAG_RD, &(et->et_quality), 0,
76             "Goodness of event timer");
77         ET_LOCK();
78         if (SLIST_EMPTY(&eventtimers) ||
79             SLIST_FIRST(&eventtimers)->et_quality < et->et_quality) {
80                 SLIST_INSERT_HEAD(&eventtimers, et, et_all);
81         } else {
82                 SLIST_FOREACH(tmp, &eventtimers, et_all) {
83                         next = SLIST_NEXT(tmp, et_all);
84                         if (next == NULL || next->et_quality < et->et_quality) {
85                                 SLIST_INSERT_AFTER(tmp, et, et_all);
86                                 break;
87                         }
88                 }
89         }
90         ET_UNLOCK();
91         return (0);
92 }
93
94 /*
95  * Deregister event timer hardware.
96  */
97 int
98 et_deregister(struct eventtimer *et)
99 {
100         int err = 0;
101
102         if (et->et_deregister_cb != NULL) {
103                 if ((err = et->et_deregister_cb(et, et->et_arg)) != 0)
104                         return (err);
105         }
106
107         ET_LOCK();
108         SLIST_REMOVE(&eventtimers, et, eventtimer, et_all);
109         ET_UNLOCK();
110         sysctl_remove_oid(et->et_sysctl, 1, 1);
111         return (0);
112 }
113
114 /*
115  * Find free event timer hardware with specified parameters.
116  */
117 struct eventtimer *
118 et_find(const char *name, int check, int want)
119 {
120         struct eventtimer *et = NULL;
121
122         SLIST_FOREACH(et, &eventtimers, et_all) {
123                 if (et->et_active)
124                         continue;
125                 if (name != NULL && strcasecmp(et->et_name, name) != 0)
126                         continue;
127                 if (name == NULL && et->et_quality < 0)
128                         continue;
129                 if ((et->et_flags & check) != want)
130                         continue;
131                 break;
132         }
133         return (et);
134 }
135
136 /*
137  * Initialize event timer hardware. Set callbacks.
138  */
139 int
140 et_init(struct eventtimer *et, et_event_cb_t *event,
141     et_deregister_cb_t *deregister, void *arg)
142 {
143
144         if (event == NULL)
145                 return (EINVAL);
146         if (et->et_active)
147                 return (EBUSY);
148
149         et->et_active = 1;
150         et->et_event_cb = event;
151         et->et_deregister_cb = deregister;
152         et->et_arg = arg;
153         return (0);
154 }
155
156 /*
157  * Start event timer hardware.
158  * first - delay before first tick.
159  * period - period of subsequent periodic ticks.
160  */
161 int
162 et_start(struct eventtimer *et,
163     struct bintime *first, struct bintime *period)
164 {
165
166         if (!et->et_active)
167                 return (ENXIO);
168         if (first == NULL && period == NULL)
169                 return (EINVAL);
170         if ((et->et_flags & ET_FLAGS_PERIODIC) == 0 &&
171             period != NULL)
172                 return (ENODEV);
173         if ((et->et_flags & ET_FLAGS_ONESHOT) == 0 &&
174             period == NULL)
175                 return (ENODEV);
176         if (first != NULL) {
177                 if (first->sec < et->et_min_period.sec ||
178                     (first->sec == et->et_min_period.sec &&
179                      first->frac < et->et_min_period.frac))
180                         first = &et->et_min_period;
181                 if (first->sec > et->et_max_period.sec ||
182                     (first->sec == et->et_max_period.sec &&
183                      first->frac > et->et_max_period.frac))
184                         first = &et->et_max_period;
185         }
186         if (period != NULL) {
187                 if (period->sec < et->et_min_period.sec ||
188                     (period->sec == et->et_min_period.sec &&
189                      period->frac < et->et_min_period.frac))
190                         period = &et->et_min_period;
191                 if (period->sec > et->et_max_period.sec ||
192                     (period->sec == et->et_max_period.sec &&
193                      period->frac > et->et_max_period.frac))
194                         period = &et->et_max_period;
195         }
196         if (et->et_start)
197                 return (et->et_start(et, first, period));
198         return (0);
199 }
200
201 /* Stop event timer hardware. */
202 int
203 et_stop(struct eventtimer *et)
204 {
205
206         if (!et->et_active)
207                 return (ENXIO);
208         if (et->et_stop)
209                 return (et->et_stop(et));
210         return (0);
211 }
212
213 /* Mark event timer hardware as broken. */
214 int
215 et_ban(struct eventtimer *et)
216 {
217
218         et->et_flags &= ~(ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT);
219         return (0);
220 }
221
222 /* Free event timer hardware. */
223 int
224 et_free(struct eventtimer *et)
225 {
226
227         if (!et->et_active)
228                 return (ENXIO);
229
230         et->et_active = 0;
231         return (0);
232 }
233
234 /* Report list of supported event timers hardware via sysctl. */
235 static int
236 sysctl_kern_eventtimer_choice(SYSCTL_HANDLER_ARGS)
237 {
238         char buf[512], *spc;
239         struct eventtimer *et;
240         int error, off;
241
242         spc = "";
243         error = 0;
244         buf[0] = 0;
245         off = 0;
246         ET_LOCK();
247         SLIST_FOREACH(et, &eventtimers, et_all) {
248                 off += snprintf(buf + off, sizeof(buf) - off, "%s%s(%d)",
249                     spc, et->et_name, et->et_quality);
250                 spc = " ";
251         }
252         ET_UNLOCK();
253         error = SYSCTL_OUT(req, buf, strlen(buf));
254         return (error);
255 }
256 SYSCTL_PROC(_kern_eventtimer, OID_AUTO, choice,
257     CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
258     0, 0, sysctl_kern_eventtimer_choice, "A", "Present event timers");
259