diff --git a/MAC/Drivers/spid/src/Makefile.am b/MAC/Drivers/spid/src/Makefile.am index b1a0e0a8434462467f6b713aafc11c0c2eef0f66..6d2466a2f8e770ab7aa681f15a3ac0f5813ba6f9 100755 --- a/MAC/Drivers/spid/src/Makefile.am +++ b/MAC/Drivers/spid/src/Makefile.am @@ -14,4 +14,9 @@ spid_o_CFLAGS = \ -D__KERNEL__ \ -DMODULE \ -I$(KERNELDIR)/include \ - -O2 + -O2 +# +# To enable the PPSAPI support in the spid driver add +# -DCONFIG_NTP_PPS_SPID to the spid_o_CFLAGS +# +# -DCONFIG_NTP_PPS_SPID diff --git a/MAC/Drivers/spid/src/spid.c b/MAC/Drivers/spid/src/spid.c index 187a7aa5d0df0c7849d7b4fb759b117ea72d4fe5..b5c0620a2e78723822c43151dafffcda6e531e89 100755 --- a/MAC/Drivers/spid/src/spid.c +++ b/MAC/Drivers/spid/src/spid.c @@ -55,9 +55,26 @@ #include <asm/io.h> /* send/receive data of port or memory */ #include <asm/uaccess.h> /* copy between userspace and kernelspace V.V. */ +/* debugging defines */ +#ifdef CONFIG_NTP_PPS_SPID +#undef DEBUG_NTP_PPS +/*#define DEBUG_NTP_PPS*/ +#endif /* CONFIG_NTP_PPS_SPID */ + +#ifdef CONFIG_NTP_PPS_SPID +#include <linux/time.h> /* struct timeval, do_gettimeofday(), + hardpps() */ +#include <linux/timepps.h> /* PPS API */ +#include <linux/slab.h> /* kmalloc(), kfree() */ +#endif + #ifdef MODULE -MODULE_AUTHOR("Tilman Muller"); +#ifndef CONFIG_NTP_PPS_SPID MODULE_DESCRIPTION("Simple Parallel (Port) Interrupt Driver for MAC"); +#else +MODULE_DESCRIPTION("Simple Parallel (Port) Interrupt Driver for MAC +PPS_API (RFC-2783)"); +#endif /* CONFIG_NTP_PPS_SPID */ +MODULE_AUTHOR("Tilman Muller"); MODULE_LICENSE("GPL"); MODULE_PARM (spid_base, "i"); @@ -122,6 +139,106 @@ struct file_operations spid_fops = { ioctl: spid_ioctl, }; +#ifdef CONFIG_NTP_PPS_SPID +#define NANOSECOND 1000000000 + +/* update PPS info from the event time stamp stored in etime and ecount. */ +static inline void pps_update_event(struct file * file) +{ + struct pps * pp = (struct pps*)file->private_data; + int mode; + + if (NULL == pp) { +#ifdef DEBUG_NTP_PPS + printk(KERN_ERR + "pps_update_event(): no pps struct"); +#endif /* DEBUG_NTP_PPS */ + return; + } + mode = pp->state.parm.mode; + if ((mode & (PPS_ECHOASSERT|PPS_ECHOCLEAR)) != 0) { + /* echo event, how? */ +#if 0 + info->MCR |= UART_MCR_RTS; /* signal no event */ + if (((modem_status & UART_MSR_DCD) != 0) == + ((mode & PPS_ECHOASSERT) != 0)) + info->MCR &= ~UART_MCR_RTS; /* signal event */ + serial_out(info, UART_MCR, info->MCR); +#endif + } + + /* get timestamp */ + /*if ((modem_status & UART_MSR_DCD) != 0)*/ + { + struct timespec ts; + ts = pp->state.etime; + pp->state.info.current_mode = mode; + if ((mode & PPS_OFFSETASSERT) != 0) { + ts.tv_nsec += pp->state.parm.assert_offset.tv_nsec; + if (ts.tv_nsec >= NANOSECOND) { + ts.tv_nsec -= NANOSECOND; + ++ts.tv_sec; + } else if (ts.tv_nsec < 0) { + ts.tv_nsec += NANOSECOND; + --ts.tv_sec; + } + } + if ((pps_kc_hardpps_mode & PPS_CAPTUREASSERT) != 0 && + pps_kc_hardpps_dev == (void *) file) { +#ifdef DEBUG_NTP_PPS + printk(KERN_INFO "spid: calling hardpps\n"); +#endif + hardpps(&ts, pp->state.ecount); + } + if ((mode & PPS_CAPTUREASSERT) != 0) { + pp->state.info.assert_timestamp = ts; + ++pp->state.info.assert_sequence; + if (waitqueue_active(&pp->state.ewait)) + wake_up_interruptible(&pp->state.ewait); + } +#ifdef DEBUG_NTP_PPS + printk(KERN_INFO + "ASSERT event #%lu for %p at %lu.%09ld (%9ld)\n", + pp->state.info.assert_sequence, pp, ts.tv_sec, + ts.tv_nsec, pp->state.ecount); +#endif /* DEBUG_NTP_PPS */ + } +#if 0 +/*else*/ { + struct timespec ts; + ts = pp->state.etime; + pp->state.info.current_mode = mode; + if ((mode & PPS_OFFSETCLEAR) != 0) { + ts.tv_nsec += pp->state.parm.clear_offset.tv_nsec; + if (ts.tv_nsec >= NANOSECOND) { + ts.tv_nsec -= NANOSECOND; + ++ts.tv_sec; + } else if (ts.tv_nsec < 0) { + ts.tv_nsec += NANOSECOND; + --ts.tv_sec; + } + } + if ((pps_kc_hardpps_mode & PPS_CAPTURECLEAR) != 0 && + pps_kc_hardpps_dev == (void *) file) + hardpps(&ts, pp->state.ecount); + if ((mode & PPS_CAPTURECLEAR) != 0) { + pp->state.info.clear_timestamp = ts; + ++pp->state.info.clear_sequence; + if (waitqueue_active(&pp->state.ewait)) + wake_up_interruptible(&pp->state.ewait); + } +#ifdef DEBUG_NTP_PPS + printk(KERN_INFO + "CLEAR event #%lu for %p at %lu.%09ld (%9ld)\n", + pp->state.info.clear_sequence, pp, ts.tv_sec, + ts.tv_nsec, pp->state.ecount); +#endif /* DEBUG_NTP_PPS */ + } +#endif /* 0 */ +#undef NANOSECOND +} +#endif + /* init and cleanup */ // Insert a Module in the Linux Kernel @@ -210,7 +327,6 @@ int spid_open (struct inode *inodeisr , struct file *filp) spid_irq = -1; } } while (spid_irq < 0 && count++ < 5); - } if (spid_irq_requested == 0) @@ -225,9 +341,18 @@ int spid_open (struct inode *inodeisr , struct file *filp) printk(KERN_INFO "spid: Found and use irq %d\n", spid_irq); inb(spid_base + 1); outb(0x10, spid_base + 2); // enables reporting the interrupt +#ifndef CONFIG_NTP_PPS_SPID request_irq(spid_irq, spid_isr, SA_INTERRUPT, "spid interrupt service routine", NULL); +#else + request_irq(spid_irq, spid_isr, SA_INTERRUPT, "spid +PPS (RFC-2783)", filp); +#endif } } + +#ifdef CONFIG_NTP_PPS_SPID + filp->private_data = NULL; /* holds pointer to the struct pps */ +#endif /* CONFIG_NTP_PPS_SPID */ + return 0; } @@ -237,12 +362,34 @@ int spid_open (struct inode *inodeisr , struct file *filp) void spid_isr(int irq, void* dev_id, struct pt_regs* regs) { - printk(KERN_INFO "spid: Interrupt\n"); + /*printk(KERN_INFO "spid: Interrupt\n");*/ outb_p(0x00, spid_base); // in some cases it seams this is needed to force a down edge of the pin 10 inb(spid_base + 1); if (MOD_IN_USE) spid_interrupt++; + +#ifdef CONFIG_NTP_PPS_SPID + struct file * file = (struct file*) dev_id; + struct pps * pps = (file ? file->private_data : NULL); + + if (NULL != pps) + { + pps_update_event(file); + + /* store timestamp in case it is wanted later */ + pps->state.ecount = do_clock_gettime(CLOCK_REALTIME, + &pps->state.etime); + +/*#ifdef DEBUG_NTP_PPS*/ + printk(KERN_INFO + "spid_isr: time %ld:%ld(%ld)\n", + pps->state.etime.tv_sec, + pps->state.etime.tv_nsec, + pps->state.ecount); +/*#endif*/ + } +#endif /* CONFIG_NTP_PPS_SPID */ } // Closing a Device because device is treated like a normal file. @@ -254,10 +401,42 @@ int spid_release (struct inode* inode, struct file* filp) MOD_DEC_USE_COUNT; if (!MOD_IN_USE) { +#ifdef CONFIG_NTP_PPS_SPID + struct pps* pps = (struct pps*)filp->private_data; + + if (NULL != pps) + { + struct pps *tmp = pps; + if (waitqueue_active(&tmp->state.ewait)) + wake_up_interruptible(&tmp->state.ewait); + /*info->tty->termios->c_line = N_TTY;*/ + filp->private_data = NULL; /*info->tty->disc_data = NULL;*/ + /*tmp->magic = 0;*/ + kfree(tmp); + if (pps_kc_hardpps_dev == (void *) filp) { + pps_kc_hardpps_mode = 0; + pps_kc_hardpps_dev = NULL; +#ifdef DEBUG_NTP_PPS + printk(KERN_INFO + "spid_release(): unbound kernel consumer dev %p\n", + filp); +#endif /* DEBUG_NTP_PPS */ + } +#ifdef DEBUG_NTP_PPS + printk(KERN_INFO "spid_release(): removed pps %p from file %p\n", + tmp, filp); +#endif /* DEBUG_NTP_PPS */ + } +#endif /* CONFIG_NTP_PPS_SPID */ + if (spid_irq > 0) { printk(KERN_INFO "spid: Release irq %d\n", spid_irq); +#ifndef CONFIG_NTP_PPS_SPID free_irq(spid_irq, NULL); +#else + free_irq(spid_irq, filp); +#endif } outb_p(0x00, spid_base + 2); // disable reporting the interrupt spid_interrupt = 0; @@ -349,10 +528,42 @@ unsigned int spid_poll(struct file* filp, poll_table* wait) return mask; } +#ifdef CONFIG_NTP_PPS_SPID +/* Return allowed mode bits for given pps struct, file's mode, and user. + * Bits set in `*obligatory' must be set. Returned bits may be set. + */ +static int pps_allowed_mode(const struct file * file, mode_t fmode, + int *obligatory) +{ + const struct pps * pps = (struct pps*)file->private_data; + int cap = pps->state.cap; + + cap &= ~PPS_CANWAIT; /* always RO */ + *obligatory = PPS_TSFMT_TSPEC; /* only supported format */ + if ((fmode & FMODE_WRITE) == 0) { /* read-only mode */ + cap = *obligatory = pps->state.parm.mode; + } else if (!capable(CAP_SYS_TIME)) { /* may not manipulate time */ + int fixed_bits; + int active_flags = pps->state.parm.mode; + + if (pps_kc_hardpps_dev == file) { + fixed_bits = PPS_OFFSETASSERT|PPS_OFFSETCLEAR; + fixed_bits &= active_flags; + *obligatory |= fixed_bits; + } + } + return cap; +} +#endif + // This method will be called on ioctl in userspace // NOTE: Only FIONREAD will be serviced to find out there is an interrupt or not int spid_ioctl(struct inode* inode, struct file* filp, unsigned int cmd, unsigned long arg) { +#ifdef CONFIG_NTP_PPS_SPID + int error = 0; +#endif + switch (cmd) { case FIONREAD: @@ -369,6 +580,386 @@ int spid_ioctl(struct inode* inode, struct file* filp, unsigned int cmd, unsigne MOD_INC_USE_COUNT; return 0; } + +#ifdef CONFIG_NTP_PPS_SPID +#ifdef DEBUG_NTP_PPS +#if 0 +#define RESTORE_FLAGS_AND_RETURN \ + do { \ + printk(KERN_INFO "ioctl() returns %d\n", error);\ + restore_flags(flags); return error;\ + } while (0); +#else +#define RESTORE_FLAGS_AND_RETURN \ + do { return error; } while (0) +#endif +#else +#define RESTORE_FLAGS_AND_RETURN \ + do { /*restore_flags(flags);*/ return error; } while (0); +#endif + + case PPS_IOC_CREATE: + { + /* initialize the tty data struct */ + struct pps *pps = filp->private_data; + + /*save_flags(flags); cli();*/ +#ifdef DEBUG_NTP_PPS + printk(KERN_INFO + "PPS_IOC_CREATE: file/data = %p/%p\n", + filp, pps); +#endif /* DEBUG_NTP_PPS */ + if (pps != NULL) { +#ifdef DEBUG_NTP_PPS + printk(KERN_INFO + "PPS_IOC_CREATE: magic = 0x%x\n", + pps->magic); +#endif /* DEBUG_NTP_PPS */ + /* share the handle if valid, otherwise fail */ + if (pps->magic != PPSCLOCK_MAGIC) + error = -EBADF; + RESTORE_FLAGS_AND_RETURN; + } + if ((filp->f_mode & FMODE_WRITE) == 0) { + error = -EBADF; + RESTORE_FLAGS_AND_RETURN; + } + if ((pps = (struct pps *) kmalloc(sizeof(struct pps), + GFP_KERNEL)) == NULL) + { + printk(KERN_ERR + "PPS_IOC_CREATE: kmalloc failed\n"); + error = -ENOMEM; + RESTORE_FLAGS_AND_RETURN; + } + /* clear all parameters */ + memset(pps, 0, sizeof(struct pps)); + pps->magic = PPSCLOCK_MAGIC; + pps->state.parm.api_version = PPS_API_VERS_1; + pps->state.parm.mode = PPS_TSFMT_TSPEC; + pps->state.cap = (PPS_CAPTUREASSERT| + PPS_CAPTURECLEAR| + PPS_OFFSETASSERT| + PPS_OFFSETCLEAR| + PPS_ECHOASSERT| + PPS_ECHOCLEAR| + PPS_CANWAIT| + PPS_TSFMT_TSPEC); + init_waitqueue_head(&pps->state.ewait); + filp->private_data = (void*)pps; + /*tty->termios->c_line = N_PPSCLOCK;*/ +#ifdef DEBUG_NTP_PPS + printk(KERN_INFO + "PPS_IOC_CREATE: new pps at %p, magic 0x%0x\n", + pps, pps->magic); +#endif /* DEBUG_NTP_PPS */ + error = 0; + RESTORE_FLAGS_AND_RETURN; + } + + case PPS_IOC_DESTROY: + { + /* draft 03 says the settings are unaffected. */ + struct pps *pps; + + /*save_flags(flags); cli();*/ + if ((filp->f_mode & FMODE_WRITE) == 0) { + error = -EBADF; + RESTORE_FLAGS_AND_RETURN; + } + pps = filp->private_data; +#ifdef DEBUG_NTP_PPS + printk(KERN_INFO + "PPS_IOC_DESTROY: file/data = %p/%p\n", + filp, pps); +#endif /* DEBUG_NTP_PPS */ + if (pps == NULL || pps->magic != PPSCLOCK_MAGIC) { + error = -EOPNOTSUPP; + RESTORE_FLAGS_AND_RETURN; + } + if (waitqueue_active(&pps->state.ewait)) { +#ifdef DEBUG_NTP_PPS + printk(KERN_INFO + "PPS_IOC_DESTROY: wait queue busy\n"); +#endif /* DEBUG_NTP_PPS */ + error = -EBUSY; + RESTORE_FLAGS_AND_RETURN; + } + error = 0; + RESTORE_FLAGS_AND_RETURN; + } + + case PPS_IOC_FETCH: + { + struct pps *pps = (struct pps*)filp->private_data; + struct pps_fetch_args parms; + long timeout; + + /*save_flags(flags); cli();*/ +#ifdef DEBUG_NTP_PPS + printk(KERN_INFO + "PPS_IOC_FETCH: file/pps = %p/%p\n", + filp, pps); +#endif /* DEBUG_NTP_PPS */ + if (pps == NULL || pps->magic != PPSCLOCK_MAGIC) { + error = -EOPNOTSUPP; + RESTORE_FLAGS_AND_RETURN; + } + if (copy_from_user(&parms, + (struct pps_fetch_args *) arg, + sizeof(struct pps_fetch_args)) + != 0) { + error = -EFAULT; + RESTORE_FLAGS_AND_RETURN; + } + if (parms.tsformat != PPS_TSFMT_TSPEC) { + error = -EINVAL; + RESTORE_FLAGS_AND_RETURN; + } + timeout = HZ * parms.timeout.tv_sec; + timeout += parms.timeout.tv_nsec / (1000000000 / HZ); + if (timeout != 0) { + /*restore_flags(flags);*/ + if (parms.timeout.tv_sec == -1) + interruptible_sleep_on(&pps->state.ewait); + else { + timeout = interruptible_sleep_on_timeout( + &pps->state.ewait, + timeout); + if (timeout <= 0) { + error = -ETIMEDOUT; + return error; + /* flags already restored */ + } + } + /*save_flags(flags); cli();*/ + pps = (struct pps*)filp->private_data; + if (pps == NULL || pps->magic != PPSCLOCK_MAGIC) + { +#ifdef DEBUG_NTP_PPS + printk(KERN_INFO "PPS_IOC_FETCH: " + "file %p lacks pps\n", + filp); +#endif /* DEBUG_NTP_PPS */ + error = -EOPNOTSUPP; + RESTORE_FLAGS_AND_RETURN; + } + if (signal_pending(current)) { + error = -EINTR; + RESTORE_FLAGS_AND_RETURN; + } + } + parms.pps_info_buf = pps->state.info; + if (copy_to_user((struct pps_fetch_args *) arg, + &parms, + sizeof(struct pps_fetch_args)) != 0) + error = -EFAULT; + RESTORE_FLAGS_AND_RETURN; + } + + case PPS_IOC_SETPARMS: + { + struct pps *pps = (struct pps*)filp->private_data; + struct pps_params parms; + int may_bits, must_bits; + + /*save_flags(flags); cli();*/ + if ((filp->f_mode & FMODE_WRITE) == 0) { + error = -EBADF; + RESTORE_FLAGS_AND_RETURN; + } +#ifdef DEBUG_NTP_PPS + printk(KERN_INFO + "PPS_IOC_SETPARAMS: file/pps = %p/%p\n", + filp, pps); +#endif /* DEBUG_NTP_PPS */ + if (pps == NULL || pps->magic != PPSCLOCK_MAGIC) { + error = -EOPNOTSUPP; + RESTORE_FLAGS_AND_RETURN; + } + if (copy_from_user(&parms, + (struct pps_params *) arg, + sizeof(struct pps_params)) != 0) { + error = -EFAULT; + RESTORE_FLAGS_AND_RETURN; + } +#ifdef DEBUG_NTP_PPS + printk(KERN_INFO "PPS_IOC_SETPARAMS: " + "vers/mode(cap) = %#x/%#x(%#x)\n", + parms.api_version, parms.mode, pps->state.cap); +#endif /* DEBUG_NTP_PPS */ + if (parms.api_version != PPS_API_VERS_1) { + error = -EINVAL; + RESTORE_FLAGS_AND_RETURN; + } + if ((parms.mode & ~pps->state.cap) != 0 ) { + error = -EINVAL; + RESTORE_FLAGS_AND_RETURN; + } + if ((parms.mode & + (PPS_TSFMT_TSPEC|PPS_TSFMT_NTPFP)) == 0 ) { + /* section 3.3 of RFC 2783 interpreted */ + parms.mode |= PPS_TSFMT_TSPEC; + } + may_bits = pps_allowed_mode(filp, filp->f_mode, + &must_bits); + if ((parms.mode & must_bits) != must_bits || + (parms.mode & ~may_bits) != 0) { + error = -EPERM; + RESTORE_FLAGS_AND_RETURN; + } + if (capable(CAP_SYS_TIME)) { + /* allow setting offsets */ + pps->state.parm = parms; + } else { + pps_params_t *ppspp = &pps->state.parm; + + ppspp->api_version = parms.api_version; + ppspp->mode = parms.mode; + /* not offsets! */ + } +#if 0 + if (parms.mode & (PPS_CAPTUREASSERT|PPS_CAPTURECLEAR)) { + /* enable interrupts */ + info->IER |= UART_IER_MSI; + info->flags |= ASYNC_LOW_LATENCY; + if (info->flags & ASYNC_INITIALIZED) { + serial_out(info, UART_IER, info->IER); +#ifdef DEBUG_NTP_PPS + printk(KERN_INFO + "PPS_IOC_SETPARAMS: IER:%02x\n", + info->IER); +#endif /* DEBUG_NTP_PPS */ + } + } +#endif + RESTORE_FLAGS_AND_RETURN; + } + + case PPS_IOC_GETPARMS: + { + const struct pps *pps = (struct pps*)filp->private_data; + + /*save_flags(flags); cli();*/ +#ifdef DEBUG_NTP_PPS + printk(KERN_INFO + "PPS_IOC_GETPARAMS: file/pps = %p/%p\n", + filp, pps); +#endif /* DEBUG_NTP_PPS */ + if (pps == NULL || pps->magic != PPSCLOCK_MAGIC) { + error = -EOPNOTSUPP; + RESTORE_FLAGS_AND_RETURN; + } + if (copy_to_user((pps_params_t *) arg, + &(pps->state.parm), + sizeof(struct pps_params)) != 0) { + error = -EFAULT; + RESTORE_FLAGS_AND_RETURN; + } + } + + case PPS_IOC_GETCAP: + { + const struct pps *pps = (struct pps*)filp->private_data; + int may_bits, must_bits; + + /*save_flags(flags); cli();*/ +#ifdef DEBUG_NTP_PPS + printk(KERN_INFO + "PPS_IOC_GETCAP: file/pps = %p/%p\n", filp, pps); +#endif /* DEBUG_NTP_PPS */ + if (pps == NULL || pps->magic != PPSCLOCK_MAGIC) { + error = -EOPNOTSUPP; + RESTORE_FLAGS_AND_RETURN; + } + may_bits = pps_allowed_mode(filp, filp->f_mode, + &must_bits); + if (copy_to_user((int *) arg, &may_bits, + sizeof(int)) != 0) + error = -EFAULT; + RESTORE_FLAGS_AND_RETURN; + } + + case PPS_IOC_KC_BIND: + { + struct pps *pps = filp->private_data; + struct pps_bind_args parms; + + /*save_flags(flags); cli();*/ + if ((filp->f_mode & FMODE_WRITE) == 0) { + error = -EBADF; + RESTORE_FLAGS_AND_RETURN; + } +#ifdef DEBUG_NTP_PPS + printk(KERN_INFO + "PPS_IOC_KC_BIND: file/pps = %p/%p\n", filp, pps); + printk(KERN_INFO + "PPS_IOC_KC_BIND: current dev/mode = %p/0x%x\n", + pps_kc_hardpps_dev, pps_kc_hardpps_mode); + +#endif /* DEBUG_NTP_PPS */ + if (pps == NULL || pps->magic != PPSCLOCK_MAGIC) + { + error = -EOPNOTSUPP; + RESTORE_FLAGS_AND_RETURN; + } + if (copy_from_user(&parms, + (struct pps_bind_args *) arg, + sizeof(struct pps_bind_args)) != 0) + { + error = -EFAULT; + RESTORE_FLAGS_AND_RETURN; + } + /* generic parameter validation */ + if (parms.tsformat != PPS_TSFMT_TSPEC || + (parms.edge & ~PPS_CAPTUREBOTH) != 0 || +#if 0 + parms.consumer < PPS_KC_HARDPPS || + parms.consumer > PPS_KC_HARDPPS_FLL || +#endif + parms.consumer != PPS_KC_HARDPPS) { + error = -EINVAL; + RESTORE_FLAGS_AND_RETURN; + } + /* permission check */ + if (!capable(CAP_SYS_TIME)) { + error = -EPERM; + RESTORE_FLAGS_AND_RETURN; + } + /* detailed parameter check */ + if (parms.edge == 0) { + if (pps_kc_hardpps_dev == NULL || + pps_kc_hardpps_dev == (void *) filp) { + pps_kc_hardpps_mode = parms.edge; + pps_kc_hardpps_dev = NULL; +#ifdef DEBUG_NTP_PPS + printk(KERN_INFO "PPS_IOC_KC_BIND: " + "unbound kernel consumer\n"); +#endif /* DEBUG_NTP_PPS */ + } else { /* another consumer bound */ + error = -EINVAL; + RESTORE_FLAGS_AND_RETURN; + } + } else { + if (pps_kc_hardpps_dev == (void *) filp || + pps_kc_hardpps_dev == NULL) { + pps_kc_hardpps_mode = parms.edge; + pps_kc_hardpps_dev = filp; +#ifdef DEBUG_NTP_PPS + printk(KERN_INFO "PPS_IOC_KC_BIND: " + "new kernel consumer: dev=%p, " + "edge=0x%x\n", + pps_kc_hardpps_dev, + pps_kc_hardpps_mode); +#endif /* DEBUG_NTP_PPS */ + } else { + error = -EINVAL; + RESTORE_FLAGS_AND_RETURN; + } + } + RESTORE_FLAGS_AND_RETURN; + } +#endif /* CONFIG_NTP_PPS_SPID */ } return -1; }