pps.c (185003) | pps.c (187576) |
---|---|
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 * 10 * This driver implements a draft-mogul-pps-api-02.txt PPS source. 11 * 12 * The input pin is pin#10 13 * The echo output pin is pin#14 14 * 15 */ 16 17#include <sys/cdefs.h> | 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 * 10 * This driver implements a draft-mogul-pps-api-02.txt PPS source. 11 * 12 * The input pin is pin#10 13 * The echo output pin is pin#14 14 * 15 */ 16 17#include <sys/cdefs.h> |
18__FBSDID("$FreeBSD: head/sys/dev/ppbus/pps.c 185003 2008-11-16 17:42:02Z jhb $"); | 18__FBSDID("$FreeBSD: head/sys/dev/ppbus/pps.c 187576 2009-01-21 23:10:06Z jhb $"); |
19 20#include <sys/param.h> | 19 20#include <sys/param.h> |
21#include <sys/lock.h> |
|
21#include <sys/kernel.h> 22#include <sys/systm.h> 23#include <sys/module.h> | 22#include <sys/kernel.h> 23#include <sys/systm.h> 24#include <sys/module.h> |
25#include <sys/sx.h> |
|
24#include <sys/bus.h> 25#include <sys/conf.h> 26#include <sys/timepps.h> 27#include <machine/bus.h> 28#include <machine/resource.h> 29#include <sys/rman.h> 30 31#include <dev/ppbus/ppbconf.h> --- 6 unchanged lines hidden (view full) --- 38 39struct pps_data { 40 struct ppb_device pps_dev; 41 struct pps_state pps[9]; 42 struct cdev *devs[9]; 43 device_t ppsdev; 44 device_t ppbus; 45 int busy; | 26#include <sys/bus.h> 27#include <sys/conf.h> 28#include <sys/timepps.h> 29#include <machine/bus.h> 30#include <machine/resource.h> 31#include <sys/rman.h> 32 33#include <dev/ppbus/ppbconf.h> --- 6 unchanged lines hidden (view full) --- 40 41struct pps_data { 42 struct ppb_device pps_dev; 43 struct pps_state pps[9]; 44 struct cdev *devs[9]; 45 device_t ppsdev; 46 device_t ppbus; 47 int busy; |
46 struct callout_handle timeout; | 48 struct callout timeout; |
47 int lastdata; 48 | 49 int lastdata; 50 |
49 struct mtx mtx; | 51 struct sx lock; |
50 struct resource *intr_resource; /* interrupt resource */ 51 void *intr_cookie; /* interrupt registration cookie */ 52}; 53 | 52 struct resource *intr_resource; /* interrupt resource */ 53 void *intr_cookie; /* interrupt registration cookie */ 54}; 55 |
54static int ppsintr(void *arg); | 56static void ppsintr(void *arg); |
55static void ppshcpoll(void *arg); 56 57#define DEVTOSOFTC(dev) \ 58 ((struct pps_data *)device_get_softc(dev)) 59 60static devclass_t pps_devclass; 61 62static d_open_t ppsopen; --- 39 unchanged lines hidden (view full) --- 102} 103 104static int 105ppsattach(device_t dev) 106{ 107 struct pps_data *sc = DEVTOSOFTC(dev); 108 device_t ppbus = device_get_parent(dev); 109 struct cdev *d; | 57static void ppshcpoll(void *arg); 58 59#define DEVTOSOFTC(dev) \ 60 ((struct pps_data *)device_get_softc(dev)) 61 62static devclass_t pps_devclass; 63 64static d_open_t ppsopen; --- 39 unchanged lines hidden (view full) --- 104} 105 106static int 107ppsattach(device_t dev) 108{ 109 struct pps_data *sc = DEVTOSOFTC(dev); 110 device_t ppbus = device_get_parent(dev); 111 struct cdev *d; |
110 int i, unit, rid = 0; | 112 int error, i, unit, rid = 0; |
111 | 113 |
112 mtx_init(&sc->mtx, device_get_nameunit(dev), "pps", MTX_SPIN); 113 | |
114 /* declare our interrupt handler */ 115 sc->intr_resource = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 116 RF_SHAREABLE); 117 118 /* interrupts seem mandatory */ | 114 /* declare our interrupt handler */ 115 sc->intr_resource = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 116 RF_SHAREABLE); 117 118 /* interrupts seem mandatory */ |
119 if (sc->intr_resource == NULL) | 119 if (sc->intr_resource == NULL) { 120 device_printf(dev, "Unable to allocate interrupt resource\n"); |
120 return (ENXIO); | 121 return (ENXIO); |
122 } |
|
121 | 123 |
124 error = bus_setup_intr(dev, sc->intr_resource, 125 INTR_TYPE_TTY | INTR_MPSAFE, NULL, ppsintr, 126 sc, &sc->intr_cookie); 127 if (error) { 128 bus_release_resource(dev, SYS_RES_IRQ, 0, sc->intr_resource); 129 device_printf(dev, "Unable to register interrupt handler\n"); 130 return (error); 131 } 132 133 sx_init(&sc->lock, "pps"); 134 ppb_init_callout(ppbus, &sc->timeout, 0); |
|
122 sc->ppsdev = dev; 123 sc->ppbus = ppbus; 124 unit = device_get_unit(ppbus); 125 d = make_dev(&pps_cdevsw, unit, 126 UID_ROOT, GID_WHEEL, 0600, PPS_NAME "%d", unit); 127 sc->devs[0] = d; 128 sc->pps[0].ppscap = PPS_CAPTUREASSERT | PPS_ECHOASSERT; 129 d->si_drv1 = sc; 130 d->si_drv2 = (void*)0; 131 pps_init(&sc->pps[0]); 132 | 135 sc->ppsdev = dev; 136 sc->ppbus = ppbus; 137 unit = device_get_unit(ppbus); 138 d = make_dev(&pps_cdevsw, unit, 139 UID_ROOT, GID_WHEEL, 0600, PPS_NAME "%d", unit); 140 sc->devs[0] = d; 141 sc->pps[0].ppscap = PPS_CAPTUREASSERT | PPS_ECHOASSERT; 142 d->si_drv1 = sc; 143 d->si_drv2 = (void*)0; 144 pps_init(&sc->pps[0]); 145 |
133 if (ppb_request_bus(ppbus, dev, PPB_DONTWAIT)) | 146 ppb_lock(ppbus); 147 if (ppb_request_bus(ppbus, dev, PPB_DONTWAIT)) { 148 ppb_unlock(ppbus); |
134 return (0); | 149 return (0); |
150 } |
|
135 136 do { 137 i = ppb_set_mode(sc->ppbus, PPB_EPP); 138 PRVERBOSE("EPP: %d %d\n", i, PPB_IN_EPP_MODE(sc->ppbus)); 139 if (i == -1) 140 break; 141 i = 0; 142 ppb_wctr(ppbus, i); --- 20 unchanged lines hidden (view full) --- 163 164 i = IRQENABLE | PCD | nINIT | SELECTIN; 165 ppb_wctr(ppbus, i); 166 PRVERBOSE("CTR = %02x (%02x)\n", ppb_rctr(ppbus), i); 167 ppstry(ppbus, 0x00, 0xff); 168 ppstry(ppbus, 0x55, 0xff); 169 ppstry(ppbus, 0xaa, 0xff); 170 ppstry(ppbus, 0xff, 0xff); | 151 152 do { 153 i = ppb_set_mode(sc->ppbus, PPB_EPP); 154 PRVERBOSE("EPP: %d %d\n", i, PPB_IN_EPP_MODE(sc->ppbus)); 155 if (i == -1) 156 break; 157 i = 0; 158 ppb_wctr(ppbus, i); --- 20 unchanged lines hidden (view full) --- 179 180 i = IRQENABLE | PCD | nINIT | SELECTIN; 181 ppb_wctr(ppbus, i); 182 PRVERBOSE("CTR = %02x (%02x)\n", ppb_rctr(ppbus), i); 183 ppstry(ppbus, 0x00, 0xff); 184 ppstry(ppbus, 0x55, 0xff); 185 ppstry(ppbus, 0xaa, 0xff); 186 ppstry(ppbus, 0xff, 0xff); |
187 ppb_unlock(ppbus); |
|
171 172 for (i = 1; i < 9; i++) { 173 d = make_dev(&pps_cdevsw, unit + 0x10000 * i, 174 UID_ROOT, GID_WHEEL, 0600, PPS_NAME "%db%d", unit, i - 1); 175 sc->devs[i] = d; 176 sc->pps[i].ppscap = PPS_CAPTUREASSERT | PPS_CAPTURECLEAR; 177 d->si_drv1 = sc; 178 d->si_drv2 = (void *)(intptr_t)i; 179 pps_init(&sc->pps[i]); 180 } | 188 189 for (i = 1; i < 9; i++) { 190 d = make_dev(&pps_cdevsw, unit + 0x10000 * i, 191 UID_ROOT, GID_WHEEL, 0600, PPS_NAME "%db%d", unit, i - 1); 192 sc->devs[i] = d; 193 sc->pps[i].ppscap = PPS_CAPTUREASSERT | PPS_CAPTURECLEAR; 194 d->si_drv1 = sc; 195 d->si_drv2 = (void *)(intptr_t)i; 196 pps_init(&sc->pps[i]); 197 } |
198 ppb_lock(ppbus); |
|
181 } while (0); 182 i = ppb_set_mode(sc->ppbus, PPB_COMPATIBLE); 183 ppb_release_bus(ppbus, dev); | 199 } while (0); 200 i = ppb_set_mode(sc->ppbus, PPB_COMPATIBLE); 201 ppb_release_bus(ppbus, dev); |
202 ppb_unlock(ppbus); |
|
184 185 return (0); 186} 187 188static int 189ppsopen(struct cdev *dev, int flags, int fmt, struct thread *td) 190{ 191 struct pps_data *sc = dev->si_drv1; | 203 204 return (0); 205} 206 207static int 208ppsopen(struct cdev *dev, int flags, int fmt, struct thread *td) 209{ 210 struct pps_data *sc = dev->si_drv1; |
211 device_t ppbus = sc->ppbus; |
|
192 int subdev = (intptr_t)dev->si_drv2; | 212 int subdev = (intptr_t)dev->si_drv2; |
193 int error, i; | 213 int i; |
194 | 214 |
215 /* 216 * The sx lock is here solely to serialize open()'s to close 217 * the race of concurrent open()'s when pps(4) doesn't own the 218 * ppbus. 219 */ 220 sx_xlock(&sc->lock); 221 ppb_lock(ppbus); |
|
195 if (!sc->busy) { 196 device_t ppsdev = sc->ppsdev; | 222 if (!sc->busy) { 223 device_t ppsdev = sc->ppsdev; |
197 device_t ppbus = sc->ppbus; | |
198 | 224 |
199 if (ppb_request_bus(ppbus, ppsdev, PPB_WAIT|PPB_INTR)) | 225 if (ppb_request_bus(ppbus, ppsdev, PPB_WAIT|PPB_INTR)) { 226 ppb_unlock(ppbus); 227 sx_xunlock(&sc->lock); |
200 return (EINTR); | 228 return (EINTR); |
201 202 /* attach the interrupt handler */ 203 if ((error = bus_setup_intr(ppsdev, sc->intr_resource, 204 (INTR_TYPE_TTY | INTR_MPSAFE), ppsintr, NULL, 205 sc, &sc->intr_cookie))) { 206 ppb_release_bus(ppbus, ppsdev); 207 return (error); | |
208 } 209 210 i = ppb_set_mode(sc->ppbus, PPB_PS2); 211 PRVERBOSE("EPP: %d %d\n", i, PPB_IN_EPP_MODE(sc->ppbus)); 212 213 i = IRQENABLE | PCD | nINIT | SELECTIN; 214 ppb_wctr(ppbus, i); 215 } 216 if (subdev > 0 && !(sc->busy & ~1)) { | 229 } 230 231 i = ppb_set_mode(sc->ppbus, PPB_PS2); 232 PRVERBOSE("EPP: %d %d\n", i, PPB_IN_EPP_MODE(sc->ppbus)); 233 234 i = IRQENABLE | PCD | nINIT | SELECTIN; 235 ppb_wctr(ppbus, i); 236 } 237 if (subdev > 0 && !(sc->busy & ~1)) { |
217 sc->timeout = timeout(ppshcpoll, sc, 1); | 238 /* XXX: Timeout of 1? hz/100 instead perhaps? */ 239 callout_reset(&sc->timeout, 1, ppshcpoll, sc); |
218 sc->lastdata = ppb_rdtr(sc->ppbus); 219 } 220 sc->busy |= (1 << subdev); | 240 sc->lastdata = ppb_rdtr(sc->ppbus); 241 } 242 sc->busy |= (1 << subdev); |
243 ppb_unlock(ppbus); 244 sx_xunlock(&sc->lock); |
|
221 return(0); 222} 223 224static int 225ppsclose(struct cdev *dev, int flags, int fmt, struct thread *td) 226{ 227 struct pps_data *sc = dev->si_drv1; 228 int subdev = (intptr_t)dev->si_drv2; 229 | 245 return(0); 246} 247 248static int 249ppsclose(struct cdev *dev, int flags, int fmt, struct thread *td) 250{ 251 struct pps_data *sc = dev->si_drv1; 252 int subdev = (intptr_t)dev->si_drv2; 253 |
254 sx_xlock(&sc->lock); |
|
230 sc->pps[subdev].ppsparam.mode = 0; /* PHK ??? */ | 255 sc->pps[subdev].ppsparam.mode = 0; /* PHK ??? */ |
256 ppb_lock(sc->ppbus); |
|
231 sc->busy &= ~(1 << subdev); 232 if (subdev > 0 && !(sc->busy & ~1)) | 257 sc->busy &= ~(1 << subdev); 258 if (subdev > 0 && !(sc->busy & ~1)) |
233 untimeout(ppshcpoll, sc, sc->timeout); | 259 callout_stop(&sc->timeout); |
234 if (!sc->busy) { 235 device_t ppsdev = sc->ppsdev; 236 device_t ppbus = sc->ppbus; 237 238 ppb_wdtr(ppbus, 0); 239 ppb_wctr(ppbus, 0); 240 | 260 if (!sc->busy) { 261 device_t ppsdev = sc->ppsdev; 262 device_t ppbus = sc->ppbus; 263 264 ppb_wdtr(ppbus, 0); 265 ppb_wctr(ppbus, 0); 266 |
241 /* Note: the interrupt handler is automatically detached */ | |
242 ppb_set_mode(ppbus, PPB_COMPATIBLE); 243 ppb_release_bus(ppbus, ppsdev); 244 } | 267 ppb_set_mode(ppbus, PPB_COMPATIBLE); 268 ppb_release_bus(ppbus, ppsdev); 269 } |
270 ppb_unlock(sc->ppbus); 271 sx_xunlock(&sc->lock); |
|
245 return(0); 246} 247 248static void 249ppshcpoll(void *arg) 250{ 251 struct pps_data *sc = arg; 252 int i, j, k, l; 253 | 272 return(0); 273} 274 275static void 276ppshcpoll(void *arg) 277{ 278 struct pps_data *sc = arg; 279 int i, j, k, l; 280 |
254 if (!(sc->busy & ~1)) 255 return; 256 mtx_lock_spin(&sc->mtx); 257 sc->timeout = timeout(ppshcpoll, sc, 1); | 281 KASSERT(sc->busy & ~1, ("pps polling w/o opened devices")); |
258 i = ppb_rdtr(sc->ppbus); 259 if (i == sc->lastdata) 260 return; 261 l = sc->lastdata ^ i; 262 k = 1; 263 for (j = 1; j < 9; j ++) { 264 if (l & k) { 265 pps_capture(&sc->pps[j]); 266 pps_event(&sc->pps[j], 267 i & k ? PPS_CAPTUREASSERT : PPS_CAPTURECLEAR); 268 } 269 k += k; 270 } 271 sc->lastdata = i; | 282 i = ppb_rdtr(sc->ppbus); 283 if (i == sc->lastdata) 284 return; 285 l = sc->lastdata ^ i; 286 k = 1; 287 for (j = 1; j < 9; j ++) { 288 if (l & k) { 289 pps_capture(&sc->pps[j]); 290 pps_event(&sc->pps[j], 291 i & k ? PPS_CAPTUREASSERT : PPS_CAPTURECLEAR); 292 } 293 k += k; 294 } 295 sc->lastdata = i; |
272 mtx_unlock_spin(&sc->mtx); | 296 callout_reset(&sc->timeout, 1, ppshcpoll, sc); |
273} 274 | 297} 298 |
275static int | 299static void |
276ppsintr(void *arg) 277{ 278 struct pps_data *sc = (struct pps_data *)arg; 279 | 300ppsintr(void *arg) 301{ 302 struct pps_data *sc = (struct pps_data *)arg; 303 |
304 ppb_assert_locked(sc->ppbus); |
|
280 pps_capture(&sc->pps[0]); 281 if (!(ppb_rstr(sc->ppbus) & nACK)) | 305 pps_capture(&sc->pps[0]); 306 if (!(ppb_rstr(sc->ppbus) & nACK)) |
282 return (FILTER_STRAY); | 307 return; 308 |
283 if (sc->pps[0].ppsparam.mode & PPS_ECHOASSERT) 284 ppb_wctr(sc->ppbus, IRQENABLE | AUTOFEED); | 309 if (sc->pps[0].ppsparam.mode & PPS_ECHOASSERT) 310 ppb_wctr(sc->ppbus, IRQENABLE | AUTOFEED); |
285 mtx_lock_spin(&sc->mtx); | |
286 pps_event(&sc->pps[0], PPS_CAPTUREASSERT); | 311 pps_event(&sc->pps[0], PPS_CAPTUREASSERT); |
287 mtx_unlock_spin(&sc->mtx); | |
288 if (sc->pps[0].ppsparam.mode & PPS_ECHOASSERT) 289 ppb_wctr(sc->ppbus, IRQENABLE); | 312 if (sc->pps[0].ppsparam.mode & PPS_ECHOASSERT) 313 ppb_wctr(sc->ppbus, IRQENABLE); |
290 return (FILTER_HANDLED); | |
291} 292 293static int 294ppsioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, struct thread *td) 295{ 296 struct pps_data *sc = dev->si_drv1; 297 int subdev = (intptr_t)dev->si_drv2; 298 int err; 299 | 314} 315 316static int 317ppsioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, struct thread *td) 318{ 319 struct pps_data *sc = dev->si_drv1; 320 int subdev = (intptr_t)dev->si_drv2; 321 int err; 322 |
300 mtx_lock_spin(&sc->mtx); | 323 ppb_lock(sc->ppbus); |
301 err = pps_ioctl(cmd, data, &sc->pps[subdev]); | 324 err = pps_ioctl(cmd, data, &sc->pps[subdev]); |
302 mtx_unlock_spin(&sc->mtx); | 325 ppb_unlock(sc->ppbus); |
303 return (err); 304} 305 306static device_method_t pps_methods[] = { 307 /* device interface */ 308 DEVMETHOD(device_identify, ppsidentify), 309 DEVMETHOD(device_probe, ppsprobe), 310 DEVMETHOD(device_attach, ppsattach), 311 312 { 0, 0 } 313}; 314 315static driver_t pps_driver = { 316 PPS_NAME, 317 pps_methods, 318 sizeof(struct pps_data), 319}; 320DRIVER_MODULE(pps, ppbus, pps_driver, pps_devclass, 0, 0); 321MODULE_DEPEND(pps, ppbus, 1, 1, 1); | 326 return (err); 327} 328 329static device_method_t pps_methods[] = { 330 /* device interface */ 331 DEVMETHOD(device_identify, ppsidentify), 332 DEVMETHOD(device_probe, ppsprobe), 333 DEVMETHOD(device_attach, ppsattach), 334 335 { 0, 0 } 336}; 337 338static driver_t pps_driver = { 339 PPS_NAME, 340 pps_methods, 341 sizeof(struct pps_data), 342}; 343DRIVER_MODULE(pps, ppbus, pps_driver, pps_devclass, 0, 0); 344MODULE_DEPEND(pps, ppbus, 1, 1, 1); |