Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

pps: serial clients support

Adds support, by using the PPS line discipline, for the PPS sources
connected with the CD (Carrier Detect) pin of a serial port.

[akpm@linux-foundation.org: fix cast size warnings]
Signed-off-by: Rodolfo Giometti <giometti@linux.it>
Cc: David Woodhouse <dwmw2@infradead.org>
Cc: Greg KH <greg@kroah.com>
Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
Cc: Alexander Gordeev <lasaine@lvk.cs.msu.su>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Rodolfo Giometti and committed by
Linus Torvalds
a0880df0 572b9adb

+172 -1
+7
drivers/pps/clients/Kconfig
··· 15 15 This driver can also be built as a module. If so, the module 16 16 will be called pps-ktimer. 17 17 18 + config PPS_CLIENT_LDISC 19 + tristate "PPS line discipline" 20 + depends on PPS 21 + help 22 + If you say yes here you get support for a PPS source connected 23 + with the CD (Carrier Detect) pin of your serial port. 24 + 18 25 endif
+1
drivers/pps/clients/Makefile
··· 3 3 # 4 4 5 5 obj-$(CONFIG_PPS_CLIENT_KTIMER) += pps-ktimer.o 6 + obj-$(CONFIG_PPS_CLIENT_LDISC) += pps-ldisc.o 6 7 7 8 ifeq ($(CONFIG_PPS_DEBUG),y) 8 9 EXTRA_CFLAGS += -DDEBUG
+154
drivers/pps/clients/pps-ldisc.c
··· 1 + /* 2 + * pps-ldisc.c -- PPS line discipline 3 + * 4 + * 5 + * Copyright (C) 2008 Rodolfo Giometti <giometti@linux.it> 6 + * 7 + * This program is free software; you can redistribute it and/or modify 8 + * it under the terms of the GNU General Public License as published by 9 + * the Free Software Foundation; either version 2 of the License, or 10 + * (at your option) any later version. 11 + * 12 + * This program is distributed in the hope that it will be useful, 13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 + * GNU General Public License for more details. 16 + * 17 + * You should have received a copy of the GNU General Public License 18 + * along with this program; if not, write to the Free Software 19 + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 + */ 21 + 22 + #include <linux/module.h> 23 + #include <linux/serial_core.h> 24 + #include <linux/tty.h> 25 + #include <linux/pps_kernel.h> 26 + 27 + #define PPS_TTY_MAGIC 0x0001 28 + 29 + static void pps_tty_dcd_change(struct tty_struct *tty, unsigned int status, 30 + struct timespec *ts) 31 + { 32 + int id = (long)tty->disc_data; 33 + struct timespec __ts; 34 + struct pps_ktime pps_ts; 35 + 36 + /* First of all we get the time stamp... */ 37 + getnstimeofday(&__ts); 38 + 39 + /* Does caller give us a timestamp? */ 40 + if (ts) { /* Yes. Let's use it! */ 41 + pps_ts.sec = ts->tv_sec; 42 + pps_ts.nsec = ts->tv_nsec; 43 + } else { /* No. Do it ourself! */ 44 + pps_ts.sec = __ts.tv_sec; 45 + pps_ts.nsec = __ts.tv_nsec; 46 + } 47 + 48 + /* Now do the PPS event report */ 49 + pps_event(id, &pps_ts, status ? PPS_CAPTUREASSERT : PPS_CAPTURECLEAR, 50 + NULL); 51 + 52 + pr_debug("PPS %s at %lu on source #%d\n", 53 + status ? "assert" : "clear", jiffies, id); 54 + } 55 + 56 + static int (*alias_n_tty_open)(struct tty_struct *tty); 57 + 58 + static int pps_tty_open(struct tty_struct *tty) 59 + { 60 + struct pps_source_info info; 61 + struct tty_driver *drv = tty->driver; 62 + int index = tty->index + drv->name_base; 63 + int ret; 64 + 65 + info.owner = THIS_MODULE; 66 + info.dev = NULL; 67 + snprintf(info.name, PPS_MAX_NAME_LEN, "%s%d", drv->driver_name, index); 68 + snprintf(info.path, PPS_MAX_NAME_LEN, "/dev/%s%d", drv->name, index); 69 + info.mode = PPS_CAPTUREBOTH | \ 70 + PPS_OFFSETASSERT | PPS_OFFSETCLEAR | \ 71 + PPS_CANWAIT | PPS_TSFMT_TSPEC; 72 + 73 + ret = pps_register_source(&info, PPS_CAPTUREBOTH | \ 74 + PPS_OFFSETASSERT | PPS_OFFSETCLEAR); 75 + if (ret < 0) { 76 + pr_err("cannot register PPS source \"%s\"\n", info.path); 77 + return ret; 78 + } 79 + tty->disc_data = (void *)(long)ret; 80 + 81 + /* Should open N_TTY ldisc too */ 82 + ret = alias_n_tty_open(tty); 83 + if (ret < 0) 84 + pps_unregister_source((long)tty->disc_data); 85 + 86 + pr_info("PPS source #%d \"%s\" added\n", ret, info.path); 87 + 88 + return 0; 89 + } 90 + 91 + static void (*alias_n_tty_close)(struct tty_struct *tty); 92 + 93 + static void pps_tty_close(struct tty_struct *tty) 94 + { 95 + int id = (long)tty->disc_data; 96 + 97 + pps_unregister_source(id); 98 + alias_n_tty_close(tty); 99 + 100 + pr_info("PPS source #%d removed\n", id); 101 + } 102 + 103 + static struct tty_ldisc_ops pps_ldisc_ops; 104 + 105 + /* 106 + * Module stuff 107 + */ 108 + 109 + static int __init pps_tty_init(void) 110 + { 111 + int err; 112 + 113 + /* Inherit the N_TTY's ops */ 114 + n_tty_inherit_ops(&pps_ldisc_ops); 115 + 116 + /* Save N_TTY's open()/close() methods */ 117 + alias_n_tty_open = pps_ldisc_ops.open; 118 + alias_n_tty_close = pps_ldisc_ops.close; 119 + 120 + /* Init PPS_TTY data */ 121 + pps_ldisc_ops.owner = THIS_MODULE; 122 + pps_ldisc_ops.magic = PPS_TTY_MAGIC; 123 + pps_ldisc_ops.name = "pps_tty"; 124 + pps_ldisc_ops.dcd_change = pps_tty_dcd_change; 125 + pps_ldisc_ops.open = pps_tty_open; 126 + pps_ldisc_ops.close = pps_tty_close; 127 + 128 + err = tty_register_ldisc(N_PPS, &pps_ldisc_ops); 129 + if (err) 130 + pr_err("can't register PPS line discipline\n"); 131 + else 132 + pr_info("PPS line discipline registered\n"); 133 + 134 + return err; 135 + } 136 + 137 + static void __exit pps_tty_cleanup(void) 138 + { 139 + int err; 140 + 141 + err = tty_unregister_ldisc(N_PPS); 142 + if (err) 143 + pr_err("can't unregister PPS line discipline\n"); 144 + else 145 + pr_info("PPS line discipline removed\n"); 146 + } 147 + 148 + module_init(pps_tty_init); 149 + module_exit(pps_tty_cleanup); 150 + 151 + MODULE_ALIAS_LDISC(N_PPS); 152 + MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>"); 153 + MODULE_DESCRIPTION("PPS TTY device driver"); 154 + MODULE_LICENSE("GPL");
+10 -1
include/linux/serial_core.h
··· 491 491 { 492 492 struct uart_state *state = uport->state; 493 493 struct tty_port *port = &state->port; 494 + struct tty_ldisc *ld = tty_ldisc_ref(port->tty); 495 + struct timespec ts; 496 + 497 + if (ld && ld->ops->dcd_change) 498 + getnstimeofday(&ts); 494 499 495 500 uport->icount.dcd++; 496 - 497 501 #ifdef CONFIG_HARD_PPS 498 502 if ((uport->flags & UPF_HARDPPS_CD) && status) 499 503 hardpps(); ··· 509 505 else if (port->tty) 510 506 tty_hangup(port->tty); 511 507 } 508 + 509 + if (ld && ld->ops->dcd_change) 510 + ld->ops->dcd_change(port->tty, status, &ts); 511 + if (ld) 512 + tty_ldisc_deref(ld); 512 513 } 513 514 514 515 /**