diff --git a/sys/dev/ppbus/ppb_base.c b/sys/dev/ppbus/ppb_base.c index 20782385acc0..b399f4cc2d17 100644 --- a/sys/dev/ppbus/ppb_base.c +++ b/sys/dev/ppbus/ppb_base.c @@ -1,244 +1,252 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 1997, 1998, 1999 Nicolas Souchu * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include "ppbus_if.h" #include MODULE_VERSION(ppbus, 1); #define DEVTOSOFTC(dev) ((struct ppb_data *)device_get_softc(dev)) /* * ppb_poll_bus() * * Polls the bus * * max is a delay in 10-milliseconds */ int ppb_poll_bus(device_t bus, int max, uint8_t mask, uint8_t status, int how) { struct ppb_data *ppb = DEVTOSOFTC(bus); int i, j, error; uint8_t r; ppb_assert_locked(bus); /* try at least up to 10ms */ for (j = 0; j < ((how & PPB_POLL) ? max : 1); j++) { for (i = 0; i < 10000; i++) { r = ppb_rstr(bus); DELAY(1); if ((r & mask) == status) return (0); } } if (!(how & PPB_POLL)) { for (i = 0; max == PPB_FOREVER || i < max-1; i++) { if ((ppb_rstr(bus) & mask) == status) return (0); /* wait 10 ms */ error = mtx_sleep((caddr_t)bus, ppb->ppc_lock, PPBPRI | (how == PPB_NOINTR ? 0 : PCATCH), "ppbpoll", hz/100); if (error != EWOULDBLOCK) return (error); } } return (EWOULDBLOCK); } /* * ppb_get_epp_protocol() * * Return the chipset EPP protocol */ int ppb_get_epp_protocol(device_t bus) { uintptr_t protocol; ppb_assert_locked(bus); BUS_READ_IVAR(device_get_parent(bus), bus, PPC_IVAR_EPP_PROTO, &protocol); return (protocol); } /* * ppb_get_mode() * */ int ppb_get_mode(device_t bus) { struct ppb_data *ppb = DEVTOSOFTC(bus); /* XXX yet device mode = ppbus mode = chipset mode */ ppb_assert_locked(bus); return (ppb->mode); } /* * ppb_set_mode() * * Set the operating mode of the chipset, return the previous mode */ int ppb_set_mode(device_t bus, int mode) { struct ppb_data *ppb = DEVTOSOFTC(bus); int old_mode = ppb_get_mode(bus); ppb_assert_locked(bus); if (PPBUS_SETMODE(device_get_parent(bus), mode)) return (-1); /* XXX yet device mode = ppbus mode = chipset mode */ ppb->mode = (mode & PPB_MASK); return (old_mode); } /* * ppb_write() * * Write charaters to the port */ int ppb_write(device_t bus, char *buf, int len, int how) { ppb_assert_locked(bus); return (PPBUS_WRITE(device_get_parent(bus), buf, len, how)); } /* * ppb_reset_epp_timeout() * * Reset the EPP timeout bit in the status register */ int ppb_reset_epp_timeout(device_t bus) { ppb_assert_locked(bus); return(PPBUS_RESET_EPP(device_get_parent(bus))); } /* * ppb_ecp_sync() * * Wait for the ECP FIFO to be empty */ int ppb_ecp_sync(device_t bus) { ppb_assert_locked(bus); return (PPBUS_ECP_SYNC(device_get_parent(bus))); } /* * ppb_get_status() * * Read the status register and update the status info */ int ppb_get_status(device_t bus, struct ppb_status *status) { uint8_t r; ppb_assert_locked(bus); r = status->status = ppb_rstr(bus); status->timeout = r & TIMEOUT; status->error = !(r & nFAULT); status->select = r & SELECT; status->paper_end = r & PERROR; status->ack = !(r & nACK); status->busy = !(r & nBUSY); return (0); } void ppb_lock(device_t bus) { struct ppb_data *ppb = DEVTOSOFTC(bus); mtx_lock(ppb->ppc_lock); } void ppb_unlock(device_t bus) { struct ppb_data *ppb = DEVTOSOFTC(bus); mtx_unlock(ppb->ppc_lock); } +struct mtx * +ppb_get_lock(device_t bus) +{ + struct ppb_data *ppb = DEVTOSOFTC(bus); + + return (ppb->ppc_lock); +} + void _ppb_assert_locked(device_t bus, const char *file, int line) { mtx_assert_(DEVTOSOFTC(bus)->ppc_lock, MA_OWNED, file, line); } void ppb_init_callout(device_t bus, struct callout *c, int flags) { struct ppb_data *ppb = DEVTOSOFTC(bus); callout_init_mtx(c, ppb->ppc_lock, flags); } int ppb_sleep(device_t bus, void *wchan, int priority, const char *wmesg, int timo) { struct ppb_data *ppb = DEVTOSOFTC(bus); return (mtx_sleep(wchan, ppb->ppc_lock, priority, wmesg, timo)); } diff --git a/sys/dev/ppbus/ppbconf.h b/sys/dev/ppbus/ppbconf.h index 6a90f5729971..723db73412f8 100644 --- a/sys/dev/ppbus/ppbconf.h +++ b/sys/dev/ppbus/ppbconf.h @@ -1,281 +1,282 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 1997, 1998, 1999 Nicolas Souchu * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ * */ #ifndef __PPBCONF_H #define __PPBCONF_H #define n(flags) (~(flags) & (flags)) /* * Parallel Port Chipset control bits. */ #define STROBE 0x01 #define AUTOFEED 0x02 #define nINIT 0x04 #define SELECTIN 0x08 #define IRQENABLE 0x10 #define PCD 0x20 #define nSTROBE n(STROBE) #define nAUTOFEED n(AUTOFEED) #define INIT n(nINIT) #define nSELECTIN n(SELECTIN) #define nPCD n(PCD) /* * Parallel Port Chipset status bits. */ #define TIMEOUT 0x01 #define nFAULT 0x08 #define SELECT 0x10 #define PERROR 0x20 #define nACK 0x40 #define nBUSY 0x80 #ifdef _KERNEL #include /* * Parallel Port Bus sleep/wakeup queue. */ #define PPBPRI (PZERO+8) /* * Parallel Port Chipset mode masks. * NIBBLE mode is supposed to be available under each other modes. */ #define PPB_COMPATIBLE 0x0 /* Centronics compatible mode */ #define PPB_NIBBLE 0x1 /* reverse 4 bit mode */ #define PPB_PS2 0x2 /* PS/2 byte mode */ #define PPB_EPP 0x4 /* EPP mode, 32 bit */ #define PPB_ECP 0x8 /* ECP mode */ /* mode aliases */ #define PPB_SPP PPB_NIBBLE|PPB_PS2 #define PPB_BYTE PPB_PS2 #define PPB_MASK 0x0f #define PPB_OPTIONS_MASK 0xf0 #define PPB_IS_EPP(mode) (mode & PPB_EPP) #define PPB_IN_EPP_MODE(bus) (PPB_IS_EPP (ppb_get_mode (bus))) #define PPB_IN_NIBBLE_MODE(bus) (ppb_get_mode (bus) & PPB_NIBBLE) #define PPB_IN_PS2_MODE(bus) (ppb_get_mode (bus) & PPB_PS2) /* * Structure to store status information. */ struct ppb_status { unsigned char status; unsigned int timeout:1; unsigned int error:1; unsigned int select:1; unsigned int paper_end:1; unsigned int ack:1; unsigned int busy:1; }; /* Parallel port bus I/O opcodes */ #define PPB_OUTSB_EPP 1 #define PPB_OUTSW_EPP 2 #define PPB_OUTSL_EPP 3 #define PPB_INSB_EPP 4 #define PPB_INSW_EPP 5 #define PPB_INSL_EPP 6 #define PPB_RDTR 7 #define PPB_RSTR 8 #define PPB_RCTR 9 #define PPB_REPP_A 10 #define PPB_REPP_D 11 #define PPB_RECR 12 #define PPB_RFIFO 13 #define PPB_WDTR 14 #define PPB_WSTR 15 #define PPB_WCTR 16 #define PPB_WEPP_A 17 #define PPB_WEPP_D 18 #define PPB_WECR 19 #define PPB_WFIFO 20 /* * How tsleep() is called in ppb_request_bus(). */ #define PPB_DONTWAIT 0 #define PPB_NOINTR 0 #define PPB_WAIT 0x1 #define PPB_INTR 0x2 #define PPB_POLL 0x4 #define PPB_FOREVER -1 /* * Microsequence stuff. */ #define PPB_MS_MAXLEN 64 /* XXX according to MS_INS_MASK */ #define PPB_MS_MAXARGS 3 /* according to MS_ARG_MASK */ /* maximum number of mode dependent * submicrosequences for in/out operations */ #define PPB_MAX_XFER 6 union ppb_insarg { int i; void *p; char *c; int (* f)(void *, char *); }; struct ppb_microseq { int opcode; /* microins. opcode */ union ppb_insarg arg[PPB_MS_MAXARGS]; /* arguments */ }; /* microseqences used for GET/PUT operations */ struct ppb_xfer { struct ppb_microseq *loop; /* the loop microsequence */ }; /* * Parallel Port Bus Device structure. */ struct ppb_data; /* see below */ struct ppb_context { int valid; /* 1 if the struct is valid */ int mode; /* XXX chipset operating mode */ struct microseq *curpc; /* pc in curmsq */ struct microseq *curmsq; /* currently executed microseqence */ }; /* * List of IVARS available to ppb device drivers */ #define PPBUS_IVAR_MODE 0 /* other fields are reserved to the ppbus internals */ struct ppb_device { const char *name; /* name of the device */ u_int flags; /* flags */ struct ppb_context ctx; /* context of the device */ /* mode dependent get msq. If NULL, * IEEE1284 code is used */ struct ppb_xfer get_xfer[PPB_MAX_XFER]; /* mode dependent put msq. If NULL, * IEEE1284 code is used */ struct ppb_xfer put_xfer[PPB_MAX_XFER]; driver_intr_t *intr_hook; void *intr_arg; }; /* EPP standards */ #define EPP_1_9 0x0 /* default */ #define EPP_1_7 0x1 /* Parallel Port Chipset IVARS */ /* elsewhere XXX */ #define PPC_IVAR_EPP_PROTO 0 #define PPC_IVAR_LOCK 1 #define PPC_IVAR_INTR_HANDLER 2 /* * Maximum size of the PnP info string */ #define PPB_PnP_STRING_SIZE 256 /* XXX */ /* * Parallel Port Bus structure. */ struct ppb_data { #define PPB_PnP_PRINTER 0 #define PPB_PnP_MODEM 1 #define PPB_PnP_NET 2 #define PPB_PnP_HDC 3 #define PPB_PnP_PCMCIA 4 #define PPB_PnP_MEDIA 5 #define PPB_PnP_FDC 6 #define PPB_PnP_PORTS 7 #define PPB_PnP_SCANNER 8 #define PPB_PnP_DIGICAM 9 #define PPB_PnP_UNKNOWN 10 int class_id; /* not a PnP device if class_id < 0 */ int state; /* current IEEE1284 state */ int error; /* last IEEE1284 error */ int mode; /* IEEE 1284-1994 mode * NIBBLE, PS2, EPP or ECP */ device_t ppb_owner; /* device which owns the bus */ struct mtx *ppc_lock; /* lock of parent device */ struct resource *ppc_irq_res; }; struct callout; typedef int (*ppc_intr_handler)(void *); extern int ppb_attach_device(device_t); extern int ppb_request_bus(device_t, device_t, int); extern int ppb_release_bus(device_t, device_t); /* bus related functions */ extern void ppb_lock(device_t); extern void ppb_unlock(device_t); +extern struct mtx *ppb_get_lock(device_t); extern void _ppb_assert_locked(device_t, const char *, int); extern void ppb_init_callout(device_t, struct callout *, int); extern int ppb_sleep(device_t, void *, int, const char *, int); extern int ppb_get_status(device_t, struct ppb_status *); extern int ppb_poll_bus(device_t, int, uint8_t, uint8_t, int); extern int ppb_reset_epp_timeout(device_t); extern int ppb_ecp_sync(device_t); extern int ppb_get_epp_protocol(device_t); extern int ppb_set_mode(device_t, int); /* returns old mode */ extern int ppb_get_mode(device_t); /* returns current mode */ extern int ppb_write(device_t, char *, int, int); #ifdef INVARIANTS #define ppb_assert_locked(dev) _ppb_assert_locked(dev, __FILE__, __LINE__) #else #define ppb_assert_locked(dev) #endif #endif /* _KERNEL */ #endif /* !__PPBCONF_H */ diff --git a/sys/dev/ppbus/pps.c b/sys/dev/ppbus/pps.c index cf0c1a1c2f07..897ae1b78bd7 100644 --- a/sys/dev/ppbus/pps.c +++ b/sys/dev/ppbus/pps.c @@ -1,345 +1,349 @@ /*- * SPDX-License-Identifier: Beerware * * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp * ---------------------------------------------------------------------------- * * * This driver implements a draft-mogul-pps-api-02.txt PPS source. * * The input pin is pin#10 * The echo output pin is pin#14 * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ppbus_if.h" #include #define PPS_NAME "pps" /* our official name */ #define PRVERBOSE(fmt, arg...) if (bootverbose) printf(fmt, ##arg); struct pps_data { struct ppb_device pps_dev; struct pps_state pps[9]; struct cdev *devs[9]; device_t ppsdev; device_t ppbus; int busy; struct callout timeout; int lastdata; struct sx lock; struct resource *intr_resource; /* interrupt resource */ void *intr_cookie; /* interrupt registration cookie */ }; static void ppsintr(void *arg); static void ppshcpoll(void *arg); #define DEVTOSOFTC(dev) \ ((struct pps_data *)device_get_softc(dev)) static devclass_t pps_devclass; static d_open_t ppsopen; static d_close_t ppsclose; static d_ioctl_t ppsioctl; static struct cdevsw pps_cdevsw = { .d_version = D_VERSION, .d_open = ppsopen, .d_close = ppsclose, .d_ioctl = ppsioctl, .d_name = PPS_NAME, }; static void ppsidentify(driver_t *driver, device_t parent) { device_t dev; dev = device_find_child(parent, PPS_NAME, -1); if (!dev) BUS_ADD_CHILD(parent, 0, PPS_NAME, -1); } static int ppstry(device_t ppbus, int send, int expect) { int i; ppb_wdtr(ppbus, send); i = ppb_rdtr(ppbus); PRVERBOSE("S: %02x E: %02x G: %02x\n", send, expect, i); return (i != expect); } static int ppsprobe(device_t ppsdev) { device_set_desc(ppsdev, "Pulse per second Timing Interface"); return (0); } static int ppsattach(device_t dev) { struct pps_data *sc = DEVTOSOFTC(dev); device_t ppbus = device_get_parent(dev); struct cdev *d; int error, i, unit, rid = 0; /* declare our interrupt handler */ sc->intr_resource = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE); /* interrupts seem mandatory */ if (sc->intr_resource == NULL) { device_printf(dev, "Unable to allocate interrupt resource\n"); return (ENXIO); } error = bus_setup_intr(dev, sc->intr_resource, INTR_TYPE_TTY | INTR_MPSAFE, NULL, ppsintr, sc, &sc->intr_cookie); if (error) { bus_release_resource(dev, SYS_RES_IRQ, 0, sc->intr_resource); device_printf(dev, "Unable to register interrupt handler\n"); return (error); } sx_init(&sc->lock, "pps"); ppb_init_callout(ppbus, &sc->timeout, 0); sc->ppsdev = dev; sc->ppbus = ppbus; unit = device_get_unit(ppbus); d = make_dev(&pps_cdevsw, unit, UID_ROOT, GID_WHEEL, 0600, PPS_NAME "%d", unit); sc->devs[0] = d; sc->pps[0].ppscap = PPS_CAPTUREASSERT | PPS_ECHOASSERT; + sc->pps[0].driver_abi = PPS_ABI_VERSION; + sc->pps[0].driver_mtx = ppb_get_lock(ppbus); d->si_drv1 = sc; d->si_drv2 = (void*)0; - pps_init(&sc->pps[0]); + pps_init_abi(&sc->pps[0]); ppb_lock(ppbus); if (ppb_request_bus(ppbus, dev, PPB_DONTWAIT)) { ppb_unlock(ppbus); return (0); } do { i = ppb_set_mode(sc->ppbus, PPB_EPP); PRVERBOSE("EPP: %d %d\n", i, PPB_IN_EPP_MODE(sc->ppbus)); if (i == -1) break; i = 0; ppb_wctr(ppbus, i); if (ppstry(ppbus, 0x00, 0x00)) break; if (ppstry(ppbus, 0x55, 0x55)) break; if (ppstry(ppbus, 0xaa, 0xaa)) break; if (ppstry(ppbus, 0xff, 0xff)) break; i = IRQENABLE | PCD | STROBE | nINIT | SELECTIN; ppb_wctr(ppbus, i); PRVERBOSE("CTR = %02x (%02x)\n", ppb_rctr(ppbus), i); if (ppstry(ppbus, 0x00, 0x00)) break; if (ppstry(ppbus, 0x55, 0x00)) break; if (ppstry(ppbus, 0xaa, 0x00)) break; if (ppstry(ppbus, 0xff, 0x00)) break; i = IRQENABLE | PCD | nINIT | SELECTIN; ppb_wctr(ppbus, i); PRVERBOSE("CTR = %02x (%02x)\n", ppb_rctr(ppbus), i); ppstry(ppbus, 0x00, 0xff); ppstry(ppbus, 0x55, 0xff); ppstry(ppbus, 0xaa, 0xff); ppstry(ppbus, 0xff, 0xff); ppb_unlock(ppbus); for (i = 1; i < 9; i++) { d = make_dev(&pps_cdevsw, unit + 0x10000 * i, UID_ROOT, GID_WHEEL, 0600, PPS_NAME "%db%d", unit, i - 1); sc->devs[i] = d; sc->pps[i].ppscap = PPS_CAPTUREASSERT | PPS_CAPTURECLEAR; + sc->pps[i].driver_abi = PPS_ABI_VERSION; + sc->pps[i].driver_mtx = ppb_get_lock(ppbus); d->si_drv1 = sc; d->si_drv2 = (void *)(intptr_t)i; - pps_init(&sc->pps[i]); + pps_init_abi(&sc->pps[i]); } ppb_lock(ppbus); } while (0); i = ppb_set_mode(sc->ppbus, PPB_COMPATIBLE); ppb_release_bus(ppbus, dev); ppb_unlock(ppbus); return (0); } static int ppsopen(struct cdev *dev, int flags, int fmt, struct thread *td) { struct pps_data *sc = dev->si_drv1; device_t ppbus = sc->ppbus; int subdev = (intptr_t)dev->si_drv2; int i; /* * The sx lock is here solely to serialize open()'s to close * the race of concurrent open()'s when pps(4) doesn't own the * ppbus. */ sx_xlock(&sc->lock); ppb_lock(ppbus); if (!sc->busy) { device_t ppsdev = sc->ppsdev; if (ppb_request_bus(ppbus, ppsdev, PPB_WAIT|PPB_INTR)) { ppb_unlock(ppbus); sx_xunlock(&sc->lock); return (EINTR); } i = ppb_set_mode(sc->ppbus, PPB_PS2); PRVERBOSE("EPP: %d %d\n", i, PPB_IN_EPP_MODE(sc->ppbus)); i = IRQENABLE | PCD | nINIT | SELECTIN; ppb_wctr(ppbus, i); } if (subdev > 0 && !(sc->busy & ~1)) { /* XXX: Timeout of 1? hz/100 instead perhaps? */ callout_reset(&sc->timeout, 1, ppshcpoll, sc); sc->lastdata = ppb_rdtr(sc->ppbus); } sc->busy |= (1 << subdev); ppb_unlock(ppbus); sx_xunlock(&sc->lock); return(0); } static int ppsclose(struct cdev *dev, int flags, int fmt, struct thread *td) { struct pps_data *sc = dev->si_drv1; int subdev = (intptr_t)dev->si_drv2; sx_xlock(&sc->lock); sc->pps[subdev].ppsparam.mode = 0; /* PHK ??? */ ppb_lock(sc->ppbus); sc->busy &= ~(1 << subdev); if (subdev > 0 && !(sc->busy & ~1)) callout_stop(&sc->timeout); if (!sc->busy) { device_t ppsdev = sc->ppsdev; device_t ppbus = sc->ppbus; ppb_wdtr(ppbus, 0); ppb_wctr(ppbus, 0); ppb_set_mode(ppbus, PPB_COMPATIBLE); ppb_release_bus(ppbus, ppsdev); } ppb_unlock(sc->ppbus); sx_xunlock(&sc->lock); return(0); } static void ppshcpoll(void *arg) { struct pps_data *sc = arg; int i, j, k, l; KASSERT(sc->busy & ~1, ("pps polling w/o opened devices")); i = ppb_rdtr(sc->ppbus); if (i == sc->lastdata) return; l = sc->lastdata ^ i; k = 1; for (j = 1; j < 9; j ++) { if (l & k) { pps_capture(&sc->pps[j]); pps_event(&sc->pps[j], i & k ? PPS_CAPTUREASSERT : PPS_CAPTURECLEAR); } k += k; } sc->lastdata = i; callout_reset(&sc->timeout, 1, ppshcpoll, sc); } static void ppsintr(void *arg) { struct pps_data *sc = (struct pps_data *)arg; ppb_assert_locked(sc->ppbus); pps_capture(&sc->pps[0]); if (!(ppb_rstr(sc->ppbus) & nACK)) return; if (sc->pps[0].ppsparam.mode & PPS_ECHOASSERT) ppb_wctr(sc->ppbus, IRQENABLE | AUTOFEED); pps_event(&sc->pps[0], PPS_CAPTUREASSERT); if (sc->pps[0].ppsparam.mode & PPS_ECHOASSERT) ppb_wctr(sc->ppbus, IRQENABLE); } static int ppsioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, struct thread *td) { struct pps_data *sc = dev->si_drv1; int subdev = (intptr_t)dev->si_drv2; int err; ppb_lock(sc->ppbus); err = pps_ioctl(cmd, data, &sc->pps[subdev]); ppb_unlock(sc->ppbus); return (err); } static device_method_t pps_methods[] = { /* device interface */ DEVMETHOD(device_identify, ppsidentify), DEVMETHOD(device_probe, ppsprobe), DEVMETHOD(device_attach, ppsattach), { 0, 0 } }; static driver_t pps_driver = { PPS_NAME, pps_methods, sizeof(struct pps_data), }; DRIVER_MODULE(pps, ppbus, pps_driver, pps_devclass, 0, 0); MODULE_DEPEND(pps, ppbus, 1, 1, 1);