pps.c revision 47640
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.18 1999/05/30 16:51:36 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 35static struct pps_data { 36 int pps_unit; 37 int pps_open; 38 struct ppb_device pps_dev; 39 struct pps_state pps; 40} *softc[NPPS]; 41 42static int npps; 43 44/* 45 * Make ourselves visible as a ppbus driver 46 */ 47 48static struct ppb_device *ppsprobe(struct ppb_data *ppb); 49static int ppsattach(struct ppb_device *dev); 50static void ppsintr(int unit); 51 52static struct ppb_driver ppsdriver = { 53 ppsprobe, ppsattach, PPS_NAME 54}; 55 56DATA_SET(ppbdriver_set, ppsdriver); 57 58static d_open_t ppsopen; 59static d_close_t ppsclose; 60static d_ioctl_t ppsioctl; 61 62#define CDEV_MAJOR 89 63static struct cdevsw pps_cdevsw = { 64 /* open */ ppsopen, 65 /* close */ ppsclose, 66 /* read */ noread, 67 /* write */ nowrite, 68 /* ioctl */ ppsioctl, 69 /* stop */ nostop, 70 /* reset */ noreset, 71 /* devtotty */ nodevtotty, 72 /* poll */ nopoll, 73 /* mmap */ nommap, 74 /* strategy */ nostrategy, 75 /* name */ PPS_NAME, 76 /* parms */ noparms, 77 /* maj */ CDEV_MAJOR, 78 /* dump */ nodump, 79 /* psize */ nopsize, 80 /* flags */ 0, 81 /* maxio */ 0, 82 /* bmaj */ -1 83}; 84 85 86static struct ppb_device * 87ppsprobe(struct ppb_data *ppb) 88{ 89 struct pps_data *sc; 90 static int once; 91 92 if (!once++) 93 cdevsw_add(&pps_cdevsw); 94 95 sc = (struct pps_data *) malloc(sizeof(struct pps_data), 96 M_TEMP, M_NOWAIT); 97 if (!sc) { 98 printf(PPS_NAME ": cannot malloc!\n"); 99 return (0); 100 } 101 bzero(sc, sizeof(struct pps_data)); 102 103 softc[npps] = sc; 104 105 sc->pps_unit = npps++; 106 107 sc->pps_dev.id_unit = sc->pps_unit; 108 sc->pps_dev.ppb = ppb; 109 sc->pps_dev.name = ppsdriver.name; 110 sc->pps_dev.intr = ppsintr; 111 112 sc->pps.ppscap = PPS_CAPTUREASSERT | PPS_ECHOASSERT; 113 pps_init(&sc->pps); 114 return (&sc->pps_dev); 115} 116 117static int 118ppsattach(struct ppb_device *dev) 119{ 120 121 /* 122 * Report ourselves 123 */ 124 printf(PPS_NAME "%d: <Pulse per second Timing Interface> on ppbus %d\n", 125 dev->id_unit, dev->ppb->ppb_link->adapter_unit); 126 127#ifdef DEVFS 128 devfs_add_devswf(&pps_cdevsw, 129 dev->id_unit, DV_CHR, 130 UID_ROOT, GID_WHEEL, 0600, PPS_NAME "%d", dev->id_unit); 131#endif 132 return (1); 133} 134 135static int 136ppsopen(dev_t dev, int flags, int fmt, struct proc *p) 137{ 138 struct pps_data *sc; 139 u_int unit = minor(dev); 140 141 if ((unit >= npps)) 142 return (ENXIO); 143 144 sc = softc[unit]; 145 146 if (!sc->pps_open) { 147 if (ppb_request_bus(&sc->pps_dev, PPB_WAIT|PPB_INTR)) 148 return (EINTR); 149 150 ppb_wctr(&sc->pps_dev, 0); 151 ppb_wctr(&sc->pps_dev, IRQENABLE); 152 sc->pps_open = 1; 153 } 154 155 return(0); 156} 157 158static int 159ppsclose(dev_t dev, int flags, int fmt, struct proc *p) 160{ 161 struct pps_data *sc = softc[minor(dev)]; 162 163 sc->pps.ppsparam.mode = 0; /* PHK ??? */ 164 165 ppb_wdtr(&sc->pps_dev, 0); 166 ppb_wctr(&sc->pps_dev, 0); 167 168 ppb_release_bus(&sc->pps_dev); 169 sc->pps_open = 0; 170 return(0); 171} 172 173static void 174ppsintr(int unit) 175{ 176 struct pps_data *sc = softc[unit]; 177 struct timecounter *tc; 178 unsigned count; 179 180 tc = timecounter; 181 count = timecounter->tc_get_timecount(tc); 182 if (!(ppb_rstr(&sc->pps_dev) & nACK)) 183 return; 184 if (sc->pps.ppsparam.mode & PPS_ECHOASSERT) 185 ppb_wctr(&sc->pps_dev, IRQENABLE | AUTOFEED); 186 pps_event(&sc->pps, tc, count, PPS_CAPTUREASSERT); 187 if (sc->pps.ppsparam.mode & PPS_ECHOASSERT) 188 ppb_wctr(&sc->pps_dev, IRQENABLE); 189} 190 191static int 192ppsioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p) 193{ 194 struct pps_data *sc = softc[minor(dev)]; 195 196 return (pps_ioctl(cmd, data, &sc->pps)); 197} 198 199