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