aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pps/clients
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pps/clients')
-rw-r--r--drivers/pps/clients/Kconfig7
-rw-r--r--drivers/pps/clients/Makefile5
-rw-r--r--drivers/pps/clients/pps-ktimer.c44
-rw-r--r--drivers/pps/clients/pps-ldisc.c59
-rw-r--r--drivers/pps/clients/pps_parport.c258
5 files changed, 313 insertions, 60 deletions
diff --git a/drivers/pps/clients/Kconfig b/drivers/pps/clients/Kconfig
index 4e801bd7254f..8520a7f4dd62 100644
--- a/drivers/pps/clients/Kconfig
+++ b/drivers/pps/clients/Kconfig
@@ -22,4 +22,11 @@ config PPS_CLIENT_LDISC
22 If you say yes here you get support for a PPS source connected 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. 23 with the CD (Carrier Detect) pin of your serial port.
24 24
25config PPS_CLIENT_PARPORT
26 tristate "Parallel port PPS client"
27 depends on PPS && PARPORT
28 help
29 If you say yes here you get support for a PPS source connected
30 with the interrupt pin of your parallel port.
31
25endif 32endif
diff --git a/drivers/pps/clients/Makefile b/drivers/pps/clients/Makefile
index 812c9b19b430..4feb7e9e71ee 100644
--- a/drivers/pps/clients/Makefile
+++ b/drivers/pps/clients/Makefile
@@ -4,7 +4,6 @@
4 4
5obj-$(CONFIG_PPS_CLIENT_KTIMER) += pps-ktimer.o 5obj-$(CONFIG_PPS_CLIENT_KTIMER) += pps-ktimer.o
6obj-$(CONFIG_PPS_CLIENT_LDISC) += pps-ldisc.o 6obj-$(CONFIG_PPS_CLIENT_LDISC) += pps-ldisc.o
7obj-$(CONFIG_PPS_CLIENT_PARPORT) += pps_parport.o
7 8
8ifeq ($(CONFIG_PPS_DEBUG),y) 9ccflags-$(CONFIG_PPS_DEBUG) := -DDEBUG
9EXTRA_CFLAGS += -DDEBUG
10endif
diff --git a/drivers/pps/clients/pps-ktimer.c b/drivers/pps/clients/pps-ktimer.c
index e7ef5b8186d0..82583b0ff82d 100644
--- a/drivers/pps/clients/pps-ktimer.c
+++ b/drivers/pps/clients/pps-ktimer.c
@@ -19,6 +19,7 @@
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */ 20 */
21 21
22#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
22 23
23#include <linux/kernel.h> 24#include <linux/kernel.h>
24#include <linux/module.h> 25#include <linux/module.h>
@@ -31,7 +32,7 @@
31 * Global variables 32 * Global variables
32 */ 33 */
33 34
34static int source; 35static struct pps_device *pps;
35static struct timer_list ktimer; 36static struct timer_list ktimer;
36 37
37/* 38/*
@@ -40,19 +41,12 @@ static struct timer_list ktimer;
40 41
41static void pps_ktimer_event(unsigned long ptr) 42static void pps_ktimer_event(unsigned long ptr)
42{ 43{
43 struct timespec __ts; 44 struct pps_event_time ts;
44 struct pps_ktime ts;
45 45
46 /* First of all we get the time stamp... */ 46 /* First of all we get the time stamp... */
47 getnstimeofday(&__ts); 47 pps_get_ts(&ts);
48 48
49 pr_info("PPS event at %lu\n", jiffies); 49 pps_event(pps, &ts, PPS_CAPTUREASSERT, NULL);
50
51 /* ... and translate it to PPS time data struct */
52 ts.sec = __ts.tv_sec;
53 ts.nsec = __ts.tv_nsec;
54
55 pps_event(source, &ts, PPS_CAPTUREASSERT, NULL);
56 50
57 mod_timer(&ktimer, jiffies + HZ); 51 mod_timer(&ktimer, jiffies + HZ);
58} 52}
@@ -61,12 +55,11 @@ static void pps_ktimer_event(unsigned long ptr)
61 * The echo function 55 * The echo function
62 */ 56 */
63 57
64static void pps_ktimer_echo(int source, int event, void *data) 58static void pps_ktimer_echo(struct pps_device *pps, int event, void *data)
65{ 59{
66 pr_info("echo %s %s for source %d\n", 60 dev_info(pps->dev, "echo %s %s\n",
67 event & PPS_CAPTUREASSERT ? "assert" : "", 61 event & PPS_CAPTUREASSERT ? "assert" : "",
68 event & PPS_CAPTURECLEAR ? "clear" : "", 62 event & PPS_CAPTURECLEAR ? "clear" : "");
69 source);
70} 63}
71 64
72/* 65/*
@@ -89,30 +82,27 @@ static struct pps_source_info pps_ktimer_info = {
89 82
90static void __exit pps_ktimer_exit(void) 83static void __exit pps_ktimer_exit(void)
91{ 84{
92 del_timer_sync(&ktimer); 85 dev_info(pps->dev, "ktimer PPS source unregistered\n");
93 pps_unregister_source(source);
94 86
95 pr_info("ktimer PPS source unregistered\n"); 87 del_timer_sync(&ktimer);
88 pps_unregister_source(pps);
96} 89}
97 90
98static int __init pps_ktimer_init(void) 91static int __init pps_ktimer_init(void)
99{ 92{
100 int ret; 93 pps = pps_register_source(&pps_ktimer_info,
101
102 ret = pps_register_source(&pps_ktimer_info,
103 PPS_CAPTUREASSERT | PPS_OFFSETASSERT); 94 PPS_CAPTUREASSERT | PPS_OFFSETASSERT);
104 if (ret < 0) { 95 if (pps == NULL) {
105 printk(KERN_ERR "cannot register ktimer source\n"); 96 pr_err("cannot register PPS source\n");
106 return ret; 97 return -ENOMEM;
107 } 98 }
108 source = ret;
109 99
110 setup_timer(&ktimer, pps_ktimer_event, 0); 100 setup_timer(&ktimer, pps_ktimer_event, 0);
111 mod_timer(&ktimer, jiffies + HZ); 101 mod_timer(&ktimer, jiffies + HZ);
112 102
113 pr_info("ktimer PPS source registered at %d\n", source); 103 dev_info(pps->dev, "ktimer PPS source registered\n");
114 104
115 return 0; 105 return 0;
116} 106}
117 107
118module_init(pps_ktimer_init); 108module_init(pps_ktimer_init);
diff --git a/drivers/pps/clients/pps-ldisc.c b/drivers/pps/clients/pps-ldisc.c
index 8e1932d29fd4..79451f2dea6a 100644
--- a/drivers/pps/clients/pps-ldisc.c
+++ b/drivers/pps/clients/pps-ldisc.c
@@ -19,6 +19,8 @@
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */ 20 */
21 21
22#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
23
22#include <linux/module.h> 24#include <linux/module.h>
23#include <linux/serial_core.h> 25#include <linux/serial_core.h>
24#include <linux/tty.h> 26#include <linux/tty.h>
@@ -27,30 +29,18 @@
27#define PPS_TTY_MAGIC 0x0001 29#define PPS_TTY_MAGIC 0x0001
28 30
29static void pps_tty_dcd_change(struct tty_struct *tty, unsigned int status, 31static void pps_tty_dcd_change(struct tty_struct *tty, unsigned int status,
30 struct timespec *ts) 32 struct pps_event_time *ts)
31{ 33{
32 int id = (long)tty->disc_data; 34 struct pps_device *pps = (struct pps_device *)tty->disc_data;
33 struct timespec __ts; 35
34 struct pps_ktime pps_ts; 36 BUG_ON(pps == NULL);
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 37
48 /* Now do the PPS event report */ 38 /* Now do the PPS event report */
49 pps_event(id, &pps_ts, status ? PPS_CAPTUREASSERT : PPS_CAPTURECLEAR, 39 pps_event(pps, ts, status ? PPS_CAPTUREASSERT :
50 NULL); 40 PPS_CAPTURECLEAR, NULL);
51 41
52 pr_debug("PPS %s at %lu on source #%d\n", 42 dev_dbg(pps->dev, "PPS %s at %lu\n",
53 status ? "assert" : "clear", jiffies, id); 43 status ? "assert" : "clear", jiffies);
54} 44}
55 45
56static int (*alias_n_tty_open)(struct tty_struct *tty); 46static int (*alias_n_tty_open)(struct tty_struct *tty);
@@ -60,6 +50,7 @@ static int pps_tty_open(struct tty_struct *tty)
60 struct pps_source_info info; 50 struct pps_source_info info;
61 struct tty_driver *drv = tty->driver; 51 struct tty_driver *drv = tty->driver;
62 int index = tty->index + drv->name_base; 52 int index = tty->index + drv->name_base;
53 struct pps_device *pps;
63 int ret; 54 int ret;
64 55
65 info.owner = THIS_MODULE; 56 info.owner = THIS_MODULE;
@@ -70,34 +61,42 @@ static int pps_tty_open(struct tty_struct *tty)
70 PPS_OFFSETASSERT | PPS_OFFSETCLEAR | \ 61 PPS_OFFSETASSERT | PPS_OFFSETCLEAR | \
71 PPS_CANWAIT | PPS_TSFMT_TSPEC; 62 PPS_CANWAIT | PPS_TSFMT_TSPEC;
72 63
73 ret = pps_register_source(&info, PPS_CAPTUREBOTH | \ 64 pps = pps_register_source(&info, PPS_CAPTUREBOTH | \
74 PPS_OFFSETASSERT | PPS_OFFSETCLEAR); 65 PPS_OFFSETASSERT | PPS_OFFSETCLEAR);
75 if (ret < 0) { 66 if (pps == NULL) {
76 pr_err("cannot register PPS source \"%s\"\n", info.path); 67 pr_err("cannot register PPS source \"%s\"\n", info.path);
77 return ret; 68 return -ENOMEM;
78 } 69 }
79 tty->disc_data = (void *)(long)ret; 70 tty->disc_data = pps;
80 71
81 /* Should open N_TTY ldisc too */ 72 /* Should open N_TTY ldisc too */
82 ret = alias_n_tty_open(tty); 73 ret = alias_n_tty_open(tty);
83 if (ret < 0) 74 if (ret < 0) {
84 pps_unregister_source((long)tty->disc_data); 75 pr_err("cannot open tty ldisc \"%s\"\n", info.path);
76 goto err_unregister;
77 }
85 78
86 pr_info("PPS source #%d \"%s\" added\n", ret, info.path); 79 dev_info(pps->dev, "source \"%s\" added\n", info.path);
87 80
88 return 0; 81 return 0;
82
83err_unregister:
84 tty->disc_data = NULL;
85 pps_unregister_source(pps);
86 return ret;
89} 87}
90 88
91static void (*alias_n_tty_close)(struct tty_struct *tty); 89static void (*alias_n_tty_close)(struct tty_struct *tty);
92 90
93static void pps_tty_close(struct tty_struct *tty) 91static void pps_tty_close(struct tty_struct *tty)
94{ 92{
95 int id = (long)tty->disc_data; 93 struct pps_device *pps = (struct pps_device *)tty->disc_data;
96 94
97 pps_unregister_source(id);
98 alias_n_tty_close(tty); 95 alias_n_tty_close(tty);
99 96
100 pr_info("PPS source #%d removed\n", id); 97 tty->disc_data = NULL;
98 dev_info(pps->dev, "removed\n");
99 pps_unregister_source(pps);
101} 100}
102 101
103static struct tty_ldisc_ops pps_ldisc_ops; 102static struct tty_ldisc_ops pps_ldisc_ops;
diff --git a/drivers/pps/clients/pps_parport.c b/drivers/pps/clients/pps_parport.c
new file mode 100644
index 000000000000..c571d6dd8f61
--- /dev/null
+++ b/drivers/pps/clients/pps_parport.c
@@ -0,0 +1,258 @@
1/*
2 * pps_parport.c -- kernel parallel port PPS client
3 *
4 *
5 * Copyright (C) 2009 Alexander Gordeev <lasaine@lvk.cs.msu.su>
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
23/*
24 * TODO:
25 * implement echo over SEL pin
26 */
27
28#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
29
30#include <linux/kernel.h>
31#include <linux/module.h>
32#include <linux/init.h>
33#include <linux/irqnr.h>
34#include <linux/time.h>
35#include <linux/parport.h>
36#include <linux/pps_kernel.h>
37
38#define DRVDESC "parallel port PPS client"
39
40/* module parameters */
41
42#define CLEAR_WAIT_MAX 100
43#define CLEAR_WAIT_MAX_ERRORS 5
44
45static unsigned int clear_wait = 100;
46MODULE_PARM_DESC(clear_wait,
47 "Maximum number of port reads when polling for signal clear,"
48 " zero turns clear edge capture off entirely");
49module_param(clear_wait, uint, 0);
50
51
52/* internal per port structure */
53struct pps_client_pp {
54 struct pardevice *pardev; /* parport device */
55 struct pps_device *pps; /* PPS device */
56 unsigned int cw; /* port clear timeout */
57 unsigned int cw_err; /* number of timeouts */
58};
59
60static inline int signal_is_set(struct parport *port)
61{
62 return (port->ops->read_status(port) & PARPORT_STATUS_ACK) != 0;
63}
64
65/* parport interrupt handler */
66static void parport_irq(void *handle)
67{
68 struct pps_event_time ts_assert, ts_clear;
69 struct pps_client_pp *dev = handle;
70 struct parport *port = dev->pardev->port;
71 unsigned int i;
72 unsigned long flags;
73
74 /* first of all we get the time stamp... */
75 pps_get_ts(&ts_assert);
76
77 if (dev->cw == 0)
78 /* clear edge capture disabled */
79 goto out_assert;
80
81 /* try capture the clear edge */
82
83 /* We have to disable interrupts here. The idea is to prevent
84 * other interrupts on the same processor to introduce random
85 * lags while polling the port. Reading from IO port is known
86 * to take approximately 1us while other interrupt handlers can
87 * take much more potentially.
88 *
89 * Interrupts won't be disabled for a long time because the
90 * number of polls is limited by clear_wait parameter which is
91 * kept rather low. So it should never be an issue.
92 */
93 local_irq_save(flags);
94 /* check the signal (no signal means the pulse is lost this time) */
95 if (!signal_is_set(port)) {
96 local_irq_restore(flags);
97 dev_err(dev->pps->dev, "lost the signal\n");
98 goto out_assert;
99 }
100
101 /* poll the port until the signal is unset */
102 for (i = dev->cw; i; i--)
103 if (!signal_is_set(port)) {
104 pps_get_ts(&ts_clear);
105 local_irq_restore(flags);
106 dev->cw_err = 0;
107 goto out_both;
108 }
109 local_irq_restore(flags);
110
111 /* timeout */
112 dev->cw_err++;
113 if (dev->cw_err >= CLEAR_WAIT_MAX_ERRORS) {
114 dev_err(dev->pps->dev, "disabled clear edge capture after %d"
115 " timeouts\n", dev->cw_err);
116 dev->cw = 0;
117 dev->cw_err = 0;
118 }
119
120out_assert:
121 /* fire assert event */
122 pps_event(dev->pps, &ts_assert,
123 PPS_CAPTUREASSERT, NULL);
124 return;
125
126out_both:
127 /* fire assert event */
128 pps_event(dev->pps, &ts_assert,
129 PPS_CAPTUREASSERT, NULL);
130 /* fire clear event */
131 pps_event(dev->pps, &ts_clear,
132 PPS_CAPTURECLEAR, NULL);
133 return;
134}
135
136/* the PPS echo function */
137static void pps_echo(struct pps_device *pps, int event, void *data)
138{
139 dev_info(pps->dev, "echo %s %s\n",
140 event & PPS_CAPTUREASSERT ? "assert" : "",
141 event & PPS_CAPTURECLEAR ? "clear" : "");
142}
143
144static void parport_attach(struct parport *port)
145{
146 struct pps_client_pp *device;
147 struct pps_source_info info = {
148 .name = KBUILD_MODNAME,
149 .path = "",
150 .mode = PPS_CAPTUREBOTH | \
151 PPS_OFFSETASSERT | PPS_OFFSETCLEAR | \
152 PPS_ECHOASSERT | PPS_ECHOCLEAR | \
153 PPS_CANWAIT | PPS_TSFMT_TSPEC,
154 .echo = pps_echo,
155 .owner = THIS_MODULE,
156 .dev = NULL
157 };
158
159 device = kzalloc(sizeof(struct pps_client_pp), GFP_KERNEL);
160 if (!device) {
161 pr_err("memory allocation failed, not attaching\n");
162 return;
163 }
164
165 device->pardev = parport_register_device(port, KBUILD_MODNAME,
166 NULL, NULL, parport_irq, PARPORT_FLAG_EXCL, device);
167 if (!device->pardev) {
168 pr_err("couldn't register with %s\n", port->name);
169 goto err_free;
170 }
171
172 if (parport_claim_or_block(device->pardev) < 0) {
173 pr_err("couldn't claim %s\n", port->name);
174 goto err_unregister_dev;
175 }
176
177 device->pps = pps_register_source(&info,
178 PPS_CAPTUREBOTH | PPS_OFFSETASSERT | PPS_OFFSETCLEAR);
179 if (device->pps == NULL) {
180 pr_err("couldn't register PPS source\n");
181 goto err_release_dev;
182 }
183
184 device->cw = clear_wait;
185
186 port->ops->enable_irq(port);
187
188 pr_info("attached to %s\n", port->name);
189
190 return;
191
192err_release_dev:
193 parport_release(device->pardev);
194err_unregister_dev:
195 parport_unregister_device(device->pardev);
196err_free:
197 kfree(device);
198}
199
200static void parport_detach(struct parport *port)
201{
202 struct pardevice *pardev = port->cad;
203 struct pps_client_pp *device;
204
205 /* FIXME: oooh, this is ugly! */
206 if (strcmp(pardev->name, KBUILD_MODNAME))
207 /* not our port */
208 return;
209
210 device = pardev->private;
211
212 port->ops->disable_irq(port);
213 pps_unregister_source(device->pps);
214 parport_release(pardev);
215 parport_unregister_device(pardev);
216 kfree(device);
217}
218
219static struct parport_driver pps_parport_driver = {
220 .name = KBUILD_MODNAME,
221 .attach = parport_attach,
222 .detach = parport_detach,
223};
224
225/* module staff */
226
227static int __init pps_parport_init(void)
228{
229 int ret;
230
231 pr_info(DRVDESC "\n");
232
233 if (clear_wait > CLEAR_WAIT_MAX) {
234 pr_err("clear_wait value should be not greater"
235 " then %d\n", CLEAR_WAIT_MAX);
236 return -EINVAL;
237 }
238
239 ret = parport_register_driver(&pps_parport_driver);
240 if (ret) {
241 pr_err("unable to register with parport\n");
242 return ret;
243 }
244
245 return 0;
246}
247
248static void __exit pps_parport_exit(void)
249{
250 parport_unregister_driver(&pps_parport_driver);
251}
252
253module_init(pps_parport_init);
254module_exit(pps_parport_exit);
255
256MODULE_AUTHOR("Alexander Gordeev <lasaine@lvk.cs.msu.su>");
257MODULE_DESCRIPTION(DRVDESC);
258MODULE_LICENSE("GPL");