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