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