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