pps.c revision 36938
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.6 1998/06/08 02:43:12 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/uio.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 PPS_HARDPPSONASSERT | 46 PPS_OFFSETASSERT | 47 PPS_ECHOASSERT | 48 PPS_TSFMT_TSPEC; 49 50static int npps; 51 52/* 53 * Make ourselves visible as a ppbus driver 54 */ 55 56static struct ppb_device *ppsprobe(struct ppb_data *ppb); 57static int ppsattach(struct ppb_device *dev); 58static void ppsintr(int unit); 59static void pps_drvinit(void *unused); 60 61static struct ppb_driver ppsdriver = { 62 ppsprobe, ppsattach, PPS_NAME 63}; 64 65DATA_SET(ppbdriver_set, ppsdriver); 66 67static d_open_t ppsopen; 68static d_close_t ppsclose; 69static d_ioctl_t ppsioctl; 70 71#define CDEV_MAJOR 89 72static struct cdevsw pps_cdevsw = 73 { ppsopen, ppsclose, noread, nowrite, 74 ppsioctl, nullstop, nullreset, nodevtotty, 75 seltrue, nommap, nostrat, PPS_NAME, 76 NULL, -1 }; 77 78static struct ppb_device * 79ppsprobe(struct ppb_data *ppb) 80{ 81 struct pps_data *sc; 82 83 sc = (struct pps_data *) malloc(sizeof(struct pps_data), 84 M_TEMP, M_NOWAIT); 85 if (!sc) { 86 printf(PPS_NAME ": cannot malloc!\n"); 87 return (0); 88 } 89 bzero(sc, sizeof(struct pps_data)); 90 91 softc[npps] = sc; 92 93 sc->pps_unit = npps++; 94 95 sc->pps_dev.id_unit = sc->pps_unit; 96 sc->pps_dev.ppb = ppb; 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 pps_params_t *pp; 184 pps_info_t *pi; 185 186 switch (cmd) { 187 case PPS_IOC_CREATE: 188 return (0); 189 case PPS_IOC_DESTROY: 190 return (0); 191 case PPS_IOC_SETPARAMS: 192 pp = (pps_params_t *)data; 193 if (pp->mode & ~ppscap) 194 return (EINVAL); 195 sc->ppsparam = *pp; 196 return (0); 197 case PPS_IOC_GETPARAMS: 198 pp = (pps_params_t *)data; 199 *pp = sc->ppsparam; 200 return (0); 201 case PPS_IOC_GETCAP: 202 *(int*)data = ppscap; 203 return (0); 204 case PPS_IOC_FETCH: 205 pi = (pps_info_t *)data; 206 *pi = sc->ppsinfo; 207 pi->current_mode = sc->ppsparam.mode; 208 return (0); 209 case PPS_IOC_WAIT: 210 return (EOPNOTSUPP); 211 default: 212 return (ENODEV); 213 } 214} 215 216 217static pps_devsw_installed = 0; 218 219static void 220pps_drvinit(void *unused) 221{ 222 dev_t dev; 223 224 if( ! pps_devsw_installed ) { 225 dev = makedev(CDEV_MAJOR, 0); 226 cdevsw_add(&dev, &pps_cdevsw, NULL); 227 pps_devsw_installed = 1; 228 } 229} 230 231SYSINIT(ppsdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,pps_drvinit,NULL) 232