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