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