pps.c revision 33323
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$
10 *
11 */
12
13#include <sys/param.h>
14#include <sys/kernel.h>
15#include <sys/sysctl.h>
16#include <sys/systm.h>
17#include <sys/conf.h>
18#include <sys/buf.h>
19#include <sys/uio.h>
20#include <sys/syslog.h>
21#ifdef DEVFS
22#include <sys/devfsext.h>
23#endif /*DEVFS*/
24#include <sys/malloc.h>
25
26#include <machine/clock.h>
27#include <machine/lpt.h>
28
29#include <dev/ppbus/ppbconf.h>
30#include "ppps.h"
31
32#define PPPPS_NAME	"pps"		/* our official name */
33
34static struct ppps_data {
35	int	ppps_unit;
36	struct	ppb_device ppps_dev;
37	struct  ppsclockev {
38		struct	timespec timestamp;
39		u_int	serial;
40	} ev;
41	int	sawtooth;
42} *softc[NPPPPS];
43
44static int nppps;
45static int sawtooth;
46
47/*
48 * Make ourselves visible as a ppbus driver
49 */
50
51static struct ppb_device	*pppsprobe(struct ppb_data *ppb);
52static int			pppsattach(struct ppb_device *dev);
53static void			pppsintr(int unit);
54static void			ppps_drvinit(void *unused);
55
56static struct ppb_driver pppsdriver = {
57    pppsprobe, pppsattach, PPPPS_NAME
58};
59
60DATA_SET(ppbdriver_set, pppsdriver);
61
62static	d_open_t	pppsopen;
63static	d_close_t	pppsclose;
64static	d_read_t	pppsread;
65static	d_write_t	pppswrite;
66
67#define CDEV_MAJOR 89
68static struct cdevsw ppps_cdevsw =
69	{ pppsopen,	pppsclose,	pppsread,	pppswrite,
70	  noioctl,	nullstop,	nullreset,	nodevtotty,
71	  seltrue,	nommap,		nostrat,	PPPPS_NAME,
72	  NULL,		-1 };
73
74static struct ppb_device *
75pppsprobe(struct ppb_data *ppb)
76{
77	struct ppps_data *sc;
78
79	sc = (struct ppps_data *) malloc(sizeof(struct ppps_data),
80							M_TEMP, M_NOWAIT);
81	if (!sc) {
82		printf(PPPPS_NAME ": cannot malloc!\n");
83		return (0);
84	}
85	bzero(sc, sizeof(struct ppps_data));
86
87	softc[nppps] = sc;
88
89	sc->ppps_unit = nppps++;
90
91	sc->ppps_dev.id_unit = sc->ppps_unit;
92	sc->ppps_dev.ppb = ppb;
93	sc->ppps_dev.intr = pppsintr;
94
95	return (&sc->ppps_dev);
96}
97
98static int
99pppsattach(struct ppb_device *dev)
100{
101	/*
102	 * Report ourselves
103	 */
104	printf(PPPPS_NAME "%d: <Pulse per second Timing Interface> on ppbus %d\n",
105	       dev->id_unit, dev->ppb->ppb_link->adapter_unit);
106
107#ifdef DEVFS
108	sc->devfs_token = devfs_add_devswf(&ppps_cdevsw,
109		dev->id_unit, DV_CHR,
110		UID_ROOT, GID_WHEEL, 0600, PPPPS_NAME "%d", dev->id_unit);
111	sc->devfs_token_ctl = devfs_add_devswf(&ppps_cdevsw,
112		dev->id_unit | LP_BYPASS, DV_CHR,
113		UID_ROOT, GID_WHEEL, 0600, PPPPS_NAME "%d.ctl", dev->id_unit);
114#endif
115
116	return (1);
117}
118
119static	int
120pppsopen(dev_t dev, int flags, int fmt, struct proc *p)
121{
122	struct ppps_data *sc;
123	u_int unit = minor(dev);
124
125	if ((unit >= nppps))
126		return (ENXIO);
127
128	sc = softc[unit];
129
130	if (ppb_request_bus(&sc->ppps_dev, PPB_WAIT|PPB_INTR))
131		return (EINTR);
132
133	ppb_wctr(&sc->ppps_dev, 0x10);
134
135	return(0);
136}
137
138static	int
139pppsclose(dev_t dev, int flags, int fmt, struct proc *p)
140{
141	struct ppps_data *sc = softc[minor(dev)];
142
143	ppb_release_bus(&sc->ppps_dev);
144	return(0);
145}
146
147static void
148pppsintr(int unit)
149{
150/*
151 * XXX: You want to thing carefully about what you actually want to do
152 * here.
153 */
154#if 0
155	struct ppps_data *sc = softc[unit];
156	struct timespec tc;
157#if 1
158	struct timeval tv;
159#endif
160
161	nanotime(&tc);
162	if (!(ppb_rstr(&sc->ppps_dev) & nACK))
163		return;
164	tc.tv_nsec -= sc->sawtooth;
165	tc.tv_nsec += 10000;
166	sc->sawtooth = 0;
167	if (tc.tv_nsec > 1000000000) {
168		tc.tv_sec++;
169		tc.tv_nsec -= 1000000000;
170	} else if (tc.tv_nsec < 0) {
171		tc.tv_sec--;
172		tc.tv_nsec += 1000000000;
173	}
174	sc->ev.timestamp = tc;
175	sc->ev.serial++;
176#if 1
177	tv.tv_sec = tc.tv_sec;
178	tv.tv_usec = tc.tv_nsec / 1000;
179	hardpps(&tv, tv.tv_usec);
180#endif
181#endif
182}
183
184static	int
185pppsread(dev_t dev, struct uio *uio, int ioflag)
186{
187	struct ppps_data *sc = softc[minor(dev)];
188	int err, c;
189
190	c = imin(uio->uio_resid, sizeof sc->ev);
191	err = uiomove(&sc->ev, c, uio);
192	return(err);
193}
194
195static	int
196pppswrite(dev_t dev, struct uio *uio, int ioflag)
197{
198	struct ppps_data *sc = softc[minor(dev)];
199	int err, c;
200
201	c = imin(uio->uio_resid, sizeof sc->sawtooth);
202	err = uiomove(&sc->sawtooth, c, uio);
203	return(err);
204}
205
206
207
208static ppps_devsw_installed = 0;
209
210static void
211ppps_drvinit(void *unused)
212{
213	dev_t dev;
214
215	if( ! ppps_devsw_installed ) {
216		dev = makedev(CDEV_MAJOR, 0);
217		cdevsw_add(&dev,&ppps_cdevsw, NULL);
218		ppps_devsw_installed = 1;
219    	}
220}
221
222SYSINIT(pppsdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,ppps_drvinit,NULL)
223