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