pps.c revision 36938
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.6 1998/06/08 02:43:12 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/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	pps_params_t *pp;
184	pps_info_t *pi;
185
186	switch (cmd) {
187	case PPS_IOC_CREATE:
188		return (0);
189	case PPS_IOC_DESTROY:
190		return (0);
191	case PPS_IOC_SETPARAMS:
192		pp = (pps_params_t *)data;
193		if (pp->mode & ~ppscap)
194			return (EINVAL);
195		sc->ppsparam = *pp;
196		return (0);
197	case PPS_IOC_GETPARAMS:
198		pp = (pps_params_t *)data;
199		*pp = sc->ppsparam;
200		return (0);
201	case PPS_IOC_GETCAP:
202		*(int*)data = ppscap;
203		return (0);
204	case PPS_IOC_FETCH:
205		pi = (pps_info_t *)data;
206		*pi = sc->ppsinfo;
207		pi->current_mode = sc->ppsparam.mode;
208		return (0);
209	case PPS_IOC_WAIT:
210		return (EOPNOTSUPP);
211	default:
212		return (ENODEV);
213	}
214}
215
216
217static pps_devsw_installed = 0;
218
219static void
220pps_drvinit(void *unused)
221{
222	dev_t dev;
223
224	if( ! pps_devsw_installed ) {
225		dev = makedev(CDEV_MAJOR, 0);
226		cdevsw_add(&dev, &pps_cdevsw, NULL);
227		pps_devsw_installed = 1;
228    	}
229}
230
231SYSINIT(ppsdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,pps_drvinit,NULL)
232