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 36 static 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 43 static 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 52 static int npps; 53 54 /* 55 * Make ourselves visible as a ppbus driver 56 */ 57 58 static struct ppb_device *ppsprobe(struct ppb_data *ppb); 59 static int ppsattach(struct ppb_device *dev); 60 static void ppsintr(int unit); 61 static void pps_drvinit(void *unused); 62 63 static struct ppb_driver ppsdriver = { 64 ppsprobe, ppsattach, PPS_NAME 65 }; 66 67 DATA_SET(ppbdriver_set, ppsdriver); 68 69 static d_open_t ppsopen; 70 static d_close_t ppsclose; 71 static d_ioctl_t ppsioctl; 72 73 #define CDEV_MAJOR 89 74 static struct cdevsw pps_cdevsw = 75 { ppsopen, ppsclose, noread, nowrite, 76 ppsioctl, nullstop, nullreset, nodevtotty, 77 seltrue, nommap, nostrat, PPS_NAME, 78 NULL, -1 }; 79 80 static struct ppb_device * 81 ppsprobe(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 105 static int 106 ppsattach(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 123 static int 124 ppsopen(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 143 static int 144 ppsclose(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 157 static void 158 ppsintr(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 190 static int 191 ppsioctl(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 198 static pps_devsw_installed = 0; 199 200 static void 201 pps_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 212 SYSINIT(ppsdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,pps_drvinit,NULL) 213