pps_ppbus.c revision 1.6 1 /* $NetBSD: pps_ppbus.c,v 1.6 2006/05/10 10:33:40 drochner Exp $ */
2
3 /*
4 * Copyright (c) 2004
5 * Matthias Drochner. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions, and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: pps_ppbus.c,v 1.6 2006/05/10 10:33:40 drochner Exp $");
31
32 #include "opt_ntp.h"
33
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/conf.h>
37 #include <sys/proc.h>
38 #include <sys/ioctl.h>
39 #include <sys/timepps.h>
40
41 #include <dev/ppbus/ppbus_base.h>
42 #include <dev/ppbus/ppbus_device.h>
43 #include <dev/ppbus/ppbus_io.h>
44 #include <dev/ppbus/ppbus_var.h>
45
46 struct pps_softc {
47 struct ppbus_device_softc pps_dev;
48 struct device *ppbus;
49 int busy;
50 pps_info_t ppsinfo;
51 pps_params_t ppsparam;
52 #ifdef PPS_SYNC
53 int hardpps;
54 #endif
55 };
56
57 static int pps_probe(struct device *, struct cfdata *, void *);
58 static void pps_attach(struct device *, struct device *, void *);
59 CFATTACH_DECL(pps, sizeof(struct pps_softc), pps_probe, pps_attach,
60 NULL, NULL);
61 extern struct cfdriver pps_cd;
62
63 static dev_type_open(ppsopen);
64 static dev_type_close(ppsclose);
65 static dev_type_ioctl(ppsioctl);
66 const struct cdevsw pps_cdevsw = {
67 ppsopen, ppsclose, noread, nowrite, ppsioctl,
68 nostop, notty, nopoll, nommap, nokqfilter
69 };
70
71 static void ppsintr(void *arg);
72
73 static int ppscap = PPS_TSFMT_TSPEC | PPS_CAPTUREASSERT | PPS_OFFSETASSERT;
74
75 static int
76 pps_probe(struct device *parent, struct cfdata *match, void *aux)
77 {
78 struct ppbus_attach_args *args = aux;
79
80 /* we need an interrupt */
81 if (!(args->capabilities & PPBUS_HAS_INTR))
82 return 0;
83
84 return 1;
85 }
86
87 static void
88 pps_attach(struct device *parent, struct device *self, void *aux)
89 {
90 struct pps_softc *sc = device_private(self);
91
92 sc->ppbus = parent;
93
94 printf("\n");
95 }
96
97 static int
98 ppsopen(dev_t dev, int flags, int fmt, struct lwp *l)
99 {
100 struct pps_softc *sc;
101 int res, weg = 0;
102
103 sc = device_lookup(&pps_cd, minor(dev));
104 if (!sc)
105 return (ENXIO);
106
107 if (sc->busy)
108 return (0);
109
110 if (ppbus_request_bus(sc->ppbus, &sc->pps_dev.sc_dev,
111 PPBUS_WAIT|PPBUS_INTR, 0))
112 return (EINTR);
113
114 ppbus_write_ivar(sc->ppbus, PPBUS_IVAR_IEEE, &weg);
115
116 /* attach the interrupt handler */
117 /* XXX priority should be set here */
118 res = ppbus_add_handler(sc->ppbus, ppsintr, sc);
119 if (res) {
120 ppbus_release_bus(sc->ppbus, &sc->pps_dev.sc_dev,
121 PPBUS_WAIT, 0);
122 return (res);
123 }
124
125 ppbus_set_mode(sc->ppbus, PPBUS_PS2, 0);
126 ppbus_wctr(sc->ppbus, IRQENABLE | PCD | nINIT | SELECTIN);
127
128 sc->busy = 1;
129 return (0);
130 }
131
132 static int
133 ppsclose(dev_t dev, int flags, int fmt, struct lwp *l)
134 {
135 struct pps_softc *sc = device_lookup(&pps_cd, minor(dev));
136 struct device *ppbus = sc->ppbus;
137
138 sc->ppsparam.mode = 0;
139 sc->busy = 0;
140 #ifdef PPS_SYNC
141 sc->hardpps = 0;
142 #endif
143 ppbus_wdtr(ppbus, 0);
144 ppbus_wctr(ppbus, 0);
145
146 ppbus_remove_handler(ppbus, ppsintr);
147 ppbus_set_mode(ppbus, PPBUS_COMPATIBLE, 0);
148 ppbus_release_bus(ppbus, &sc->pps_dev.sc_dev, PPBUS_WAIT, 0);
149 return (0);
150 }
151
152 static void
153 ppsintr(void *arg)
154 {
155 struct pps_softc *sc = arg;
156 struct device *ppbus = sc->ppbus;
157 struct timeval tv;
158
159 if (!(ppbus_rstr(ppbus) & nACK))
160 return;
161
162 microtime(&tv);
163 TIMEVAL_TO_TIMESPEC(&tv, &sc->ppsinfo.assert_timestamp);
164 if (sc->ppsparam.mode & PPS_OFFSETASSERT) {
165 timespecadd(&sc->ppsinfo.assert_timestamp,
166 &sc->ppsparam.assert_offset,
167 &sc->ppsinfo.assert_timestamp);
168 }
169 #ifdef PPS_SYNC
170 if (sc->hardpps)
171 hardpps(&tv, tv.tv_usec);
172 #endif
173 sc->ppsinfo.assert_sequence++;
174 sc->ppsinfo.current_mode = sc->ppsparam.mode;
175 }
176
177 static int
178 ppsioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct lwp *l)
179 {
180 struct pps_softc *sc = device_lookup(&pps_cd, minor(dev));
181 int error = 0;
182
183 switch (cmd) {
184 case PPS_IOC_CREATE:
185 break;
186
187 case PPS_IOC_DESTROY:
188 break;
189
190 case PPS_IOC_GETPARAMS: {
191 pps_params_t *pp;
192 pp = (pps_params_t *)data;
193 *pp = sc->ppsparam;
194 break;
195 }
196
197 case PPS_IOC_SETPARAMS: {
198 pps_params_t *pp;
199 pp = (pps_params_t *)data;
200 if (pp->mode & ~(ppscap)) {
201 error = EINVAL;
202 break;
203 }
204 sc->ppsparam = *pp;
205 break;
206 }
207
208 case PPS_IOC_GETCAP:
209 *(int*)data = ppscap;
210 break;
211
212 case PPS_IOC_FETCH: {
213 pps_info_t *pi;
214 pi = (pps_info_t *)data;
215 *pi = sc->ppsinfo;
216 break;
217 }
218
219 #ifdef PPS_SYNC
220 case PPS_IOC_KCBIND:
221 if (*(int *)data & PPS_CAPTUREASSERT)
222 sc->hardpps = 1;
223 else
224 sc->hardpps = 0;
225 break;
226 #endif
227
228 default:
229 error = EPASSTHROUGH;
230 break;
231 }
232 return (error);
233 }
234