pps.c revision 43433
1/*
2 * ----------------------------------------------------------------------------
3 * "THE BEER-WARE LICENSE" (Revision 42):
4 * <phk@FreeBSD.org> wrote this file.  As long as you retain this notice you
5 * can do whatever you want with this stuff. If we meet some day, and you think
6 * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
7 * ----------------------------------------------------------------------------
8 *
9 * $Id: pps.c,v 1.12 1998/12/07 21:58:16 archie Exp $
10 *
11 * This driver implements a draft-mogul-pps-api-02.txt PPS source.
12 *
13 * The input pin is pin#10
14 * The echo output pin is pin#14
15 *
16 */
17
18#include "opt_devfs.h"
19#include "opt_ntp.h"
20
21#include <sys/param.h>
22#include <sys/kernel.h>
23#include <sys/systm.h>
24#include <sys/conf.h>
25#include <sys/timepps.h>
26#ifdef DEVFS
27#include <sys/devfsext.h>
28#endif
29#include <sys/malloc.h>
30
31#include <dev/ppbus/ppbconf.h>
32#include "pps.h"
33
34#define PPS_NAME	"lppps"		/* our official name */
35
36static struct pps_data {
37	int	pps_unit;
38	struct	ppb_device pps_dev;
39	pps_params_t	ppsparam;
40	pps_info_t	ppsinfo;
41} *softc[NPPS];
42
43static int ppscap =
44	PPS_CAPTUREASSERT |
45#ifdef PPS_SYNC
46	PPS_HARDPPSONASSERT |
47#endif /* PPS_SYNC */
48	PPS_OFFSETASSERT |
49	PPS_ECHOASSERT |
50	PPS_TSFMT_TSPEC;
51
52static int npps;
53
54/*
55 * Make ourselves visible as a ppbus driver
56 */
57
58static struct ppb_device	*ppsprobe(struct ppb_data *ppb);
59static int			ppsattach(struct ppb_device *dev);
60static void			ppsintr(int unit);
61static void			pps_drvinit(void *unused);
62
63static struct ppb_driver ppsdriver = {
64    ppsprobe, ppsattach, PPS_NAME
65};
66
67DATA_SET(ppbdriver_set, ppsdriver);
68
69static	d_open_t	ppsopen;
70static	d_close_t	ppsclose;
71static	d_ioctl_t	ppsioctl;
72
73#define CDEV_MAJOR 89
74static struct cdevsw pps_cdevsw =
75	{ ppsopen,	ppsclose,	noread,		nowrite,
76	  ppsioctl,	nullstop,	nullreset,	nodevtotty,
77	  seltrue,	nommap,		nostrat,	PPS_NAME,
78	  NULL,		-1 };
79
80static struct ppb_device *
81ppsprobe(struct ppb_data *ppb)
82{
83	struct pps_data *sc;
84
85	sc = (struct pps_data *) malloc(sizeof(struct pps_data),
86							M_TEMP, M_NOWAIT);
87	if (!sc) {
88		printf(PPS_NAME ": cannot malloc!\n");
89		return (0);
90	}
91	bzero(sc, sizeof(struct pps_data));
92
93	softc[npps] = sc;
94
95	sc->pps_unit = npps++;
96
97	sc->pps_dev.id_unit = sc->pps_unit;
98	sc->pps_dev.ppb = ppb;
99	sc->pps_dev.name = ppsdriver.name;
100	sc->pps_dev.intr = ppsintr;
101
102	return (&sc->pps_dev);
103}
104
105static int
106ppsattach(struct ppb_device *dev)
107{
108	/*
109	 * Report ourselves
110	 */
111	printf(PPS_NAME "%d: <Pulse per second Timing Interface> on ppbus %d\n",
112	       dev->id_unit, dev->ppb->ppb_link->adapter_unit);
113
114#ifdef DEVFS
115	devfs_add_devswf(&pps_cdevsw,
116		dev->id_unit, DV_CHR,
117		UID_ROOT, GID_WHEEL, 0600, PPS_NAME "%d", dev->id_unit);
118#endif
119
120	return (1);
121}
122
123static	int
124ppsopen(dev_t dev, int flags, int fmt, struct proc *p)
125{
126	struct pps_data *sc;
127	u_int unit = minor(dev);
128
129	if ((unit >= npps))
130		return (ENXIO);
131
132	sc = softc[unit];
133
134	if (ppb_request_bus(&sc->pps_dev, PPB_WAIT|PPB_INTR))
135		return (EINTR);
136
137	ppb_wctr(&sc->pps_dev, 0);
138	ppb_wctr(&sc->pps_dev, IRQENABLE);
139
140	return(0);
141}
142
143static	int
144ppsclose(dev_t dev, int flags, int fmt, struct proc *p)
145{
146	struct pps_data *sc = softc[minor(dev)];
147
148	sc->ppsparam.mode = 0;
149
150	ppb_wdtr(&sc->pps_dev, 0);
151	ppb_wctr(&sc->pps_dev, 0);
152
153	ppb_release_bus(&sc->pps_dev);
154	return(0);
155}
156
157static void
158ppsintr(int unit)
159{
160	struct pps_data *sc = softc[unit];
161	struct timespec tc;
162
163	nanotime(&tc);
164	if (!(ppb_rstr(&sc->pps_dev) & nACK))
165		return;
166	if (sc->ppsparam.mode & PPS_ECHOASSERT)
167		ppb_wctr(&sc->pps_dev, IRQENABLE | AUTOFEED);
168	if (sc->ppsparam.mode & PPS_OFFSETASSERT) {
169		timespecadd(&tc, &sc->ppsparam.assert_offset);
170		if (tc.tv_nsec < 0) {
171			tc.tv_sec--;
172			tc.tv_nsec += 1000000000;
173		}
174	}
175	sc->ppsinfo.assert_timestamp = tc;
176	sc->ppsinfo.assert_sequence++;
177#ifdef PPS_SYNC
178	if (sc->ppsparam.mode & PPS_HARDPPSONASSERT) {
179		struct timeval tv;
180
181		tv.tv_sec = tc.tv_sec;
182		tv.tv_usec = tc.tv_nsec / 1000;
183		hardpps(&tv, tv.tv_usec);
184	}
185#endif /* PPS_SYNC */
186	if (sc->ppsparam.mode & PPS_ECHOASSERT)
187		ppb_wctr(&sc->pps_dev, IRQENABLE);
188}
189
190static int
191ppsioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p)
192{
193	struct pps_data *sc = softc[minor(dev)];
194
195	return (std_pps_ioctl(cmd, data, &sc->ppsparam, &sc->ppsinfo, ppscap));
196}
197
198static pps_devsw_installed = 0;
199
200static void
201pps_drvinit(void *unused)
202{
203	dev_t dev;
204
205	if( ! pps_devsw_installed ) {
206		dev = makedev(CDEV_MAJOR, 0);
207		cdevsw_add(&dev, &pps_cdevsw, NULL);
208		pps_devsw_installed = 1;
209    	}
210}
211
212SYSINIT(ppsdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,pps_drvinit,NULL)
213