pps.c revision 38061
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.9 1998/06/21 18:02:32 bde 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	PPS_HARDPPSONASSERT |
45	PPS_OFFSETASSERT |
46	PPS_ECHOASSERT |
47	PPS_TSFMT_TSPEC;
48
49static int npps;
50
51/*
52 * Make ourselves visible as a ppbus driver
53 */
54
55static struct ppb_device	*ppsprobe(struct ppb_data *ppb);
56static int			ppsattach(struct ppb_device *dev);
57static void			ppsintr(int unit);
58static void			pps_drvinit(void *unused);
59
60static struct ppb_driver ppsdriver = {
61    ppsprobe, ppsattach, PPS_NAME
62};
63
64DATA_SET(ppbdriver_set, ppsdriver);
65
66static	d_open_t	ppsopen;
67static	d_close_t	ppsclose;
68static	d_ioctl_t	ppsioctl;
69
70#define CDEV_MAJOR 89
71static struct cdevsw pps_cdevsw =
72	{ ppsopen,	ppsclose,	noread,		nowrite,
73	  ppsioctl,	nullstop,	nullreset,	nodevtotty,
74	  seltrue,	nommap,		nostrat,	PPS_NAME,
75	  NULL,		-1 };
76
77static struct ppb_device *
78ppsprobe(struct ppb_data *ppb)
79{
80	struct pps_data *sc;
81
82	sc = (struct pps_data *) malloc(sizeof(struct pps_data),
83							M_TEMP, M_NOWAIT);
84	if (!sc) {
85		printf(PPS_NAME ": cannot malloc!\n");
86		return (0);
87	}
88	bzero(sc, sizeof(struct pps_data));
89
90	softc[npps] = sc;
91
92	sc->pps_unit = npps++;
93
94	sc->pps_dev.id_unit = sc->pps_unit;
95	sc->pps_dev.ppb = ppb;
96	sc->pps_dev.name = ppsdriver.name;
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