This patchset contains several changes that improve an overall design/performance of PPS subsystem. I'd like these patches to be merged mainline if no one objects. Patches 1-3 are bugfixes. Patches 4-11 are other improvements to PPS subsystem. Patches 12-14 add kernel consumer support. Patch 15 adds parallel port PPS client. Patch 16 adds parallel port PPS generator. You can find description for my previous patchset (it describes patches 12-16 in more detailed) here: http://lkml.org/lkml/2010/2/24/189 This patchset is tested against the vanilla 2.6.35 kernel. But we are actually using it on 2.6.33.7-rt29 rt-preempt kernel most of the time. Those who are interested in other versions of the patchset can find them in my git repository: git://github.com/ago/linux-2.6.git There is one problem however: kernel consumer works bad (if enabled) when CONFIG_NO_HZ is enabled. The reason for this is commit a092ff0f90cae22b2ac8028ecd2c6f6c1a9e4601. Without it hardpps() is able to sync to 1us precision in about 10 seconds. With CONFIG_NO_HZ it is not syncing at all. This only affects patches 12-14, others are ok. 2John Stultz: the problem is not with logarithmic accumulation because it works good if NTP_INTERVAL_FREQ is set to 2. Changelog v2 -> v3: * add patches 1-11 * add clear_wait parameter to pps_parport * add delay parameter to pps_gen_parport * fix seqlock unlocking * fix issues pointed out by Rodolfo Giometti: * move CONFIG_NTP_PPS to drivers/pps/Kconfig * swap parport client and generator patches * style and typo fixes * move patch that adds unified timestamp gathering to be the beginning v1 -> v2: * fix issues pointed out by John Stultz: * style fixes * add a about the authorship of the original code * replace timespec with pps_normtime struct where timespec is used in a wrong way * fix seqlock usage in hardpps() * unbind kernel consumer on device removal * send raw timestamp instead of the last difference to hardpps() ...
Since we now have direct pointers to struct pps_device everywhere it's
easy to use dev_* functions to print messages instead of plain printks.
Where dev_* cannot be used printks are converted to pr_*.
Signed-off-by: Alexander Gordeev <lasaine@lvk.cs.msu.su>
---
drivers/pps/clients/pps-ktimer.c | 4 ++--
drivers/pps/kapi.c | 14 +++++++-------
drivers/pps/pps.c | 24 ++++++++++++------------
3 files changed, 21 insertions(+), 21 deletions(-)
diff --git a/drivers/pps/clients/pps-ktimer.c b/drivers/pps/clients/pps-ktimer.c
index 64cba1d..9d27239 100644
--- a/drivers/pps/clients/pps-ktimer.c
+++ b/drivers/pps/clients/pps-ktimer.c
@@ -45,7 +45,7 @@ static void pps_ktimer_event(unsigned long ptr)
/* First of all we get the time stamp... */
pps_get_ts(&ts);
- pr_info("PPS event at %lu\n", jiffies);
+ dev_info(pps->dev, "PPS event at %lu\n", jiffies);
pps_event(pps, &ts, PPS_CAPTUREASSERT, NULL);
@@ -94,7 +94,7 @@ static int __init pps_ktimer_init(void)
pps = pps_register_source(&pps_ktimer_info,
PPS_CAPTUREASSERT | PPS_OFFSETASSERT);
if (pps == NULL) {
- printk(KERN_ERR "cannot register ktimer source\n");
+ pr_err("cannot register ktimer source\n");
return -ENOMEM;
}
diff --git a/drivers/pps/kapi.c b/drivers/pps/kapi.c
index 568223b..eddf41e 100644
--- a/drivers/pps/kapi.c
+++ b/drivers/pps/kapi.c
@@ -80,20 +80,20 @@ struct pps_device *pps_register_source(struct pps_source_info *info,
/* Sanity checks */
if ((info->mode & default_params) != default_params) {
- printk(KERN_ERR "pps: %s: unsupported default parameters\n",
+ pr_err("pps: %s: unsupported default parameters\n",
info->name);
err = -EINVAL;
goto pps_register_source_exit;
}
if ((info->mode & (PPS_ECHOASSERT | PPS_ECHOCLEAR)) != 0 &&
info->echo == NULL) {
- printk(KERN_ERR "pps: %s: echo function is not defined\n",
+ pr_err("pps: %s: echo function is not defined\n",
info->name);
err = -EINVAL;
...Add a helper function to gather timestamps. This way clients don't have
to duplicate it.
Signed-off-by: Alexander Gordeev <lasaine@lvk.cs.msu.su>
---
drivers/pps/clients/pps-ktimer.c | 9 ++-------
drivers/pps/clients/pps-ldisc.c | 18 ++++++------------
drivers/pps/kapi.c | 19 ++++++++++++-------
include/linux/pps_kernel.h | 19 ++++++++++++++++++-
include/linux/serial_core.h | 5 +++--
include/linux/tty_ldisc.h | 5 +++--
6 files changed, 44 insertions(+), 31 deletions(-)
diff --git a/drivers/pps/clients/pps-ktimer.c b/drivers/pps/clients/pps-ktimer.c
index e7ef5b8..e1bdd8b 100644
--- a/drivers/pps/clients/pps-ktimer.c
+++ b/drivers/pps/clients/pps-ktimer.c
@@ -40,18 +40,13 @@ static struct timer_list ktimer;
static void pps_ktimer_event(unsigned long ptr)
{
- struct timespec __ts;
- struct pps_ktime ts;
+ struct pps_event_time ts;
/* First of all we get the time stamp... */
- getnstimeofday(&__ts);
+ pps_get_ts(&ts);
pr_info("PPS event at %lu\n", jiffies);
- /* ... and translate it to PPS time data struct */
- ts.sec = __ts.tv_sec;
- ts.nsec = __ts.tv_nsec;
-
pps_event(source, &ts, PPS_CAPTUREASSERT, NULL);
mod_timer(&ktimer, jiffies + HZ);
diff --git a/drivers/pps/clients/pps-ldisc.c b/drivers/pps/clients/pps-ldisc.c
index 8e1932d..20fc9f7 100644
--- a/drivers/pps/clients/pps-ldisc.c
+++ b/drivers/pps/clients/pps-ldisc.c
@@ -27,26 +27,20 @@
#define PPS_TTY_MAGIC 0x0001
static void pps_tty_dcd_change(struct tty_struct *tty, unsigned int status,
- struct timespec *ts)
+ struct pps_event_time *ts)
{
int id = (long)tty->disc_data;
- struct timespec __ts;
- struct pps_ktime pps_ts;
+ struct pps_event_time __ts;
/* First of all we get the time stamp... */
- getnstimeofday(&__ts);
+ pps_get_ts(&__ts);
/* Does caller give us a timestamp? */
- if (ts) { /* Yes. Let's use it! */
- pps_ts.sec = ts->tv_sec;
- pps_ts.nsec = ts->tv_nsec;
- } else { /* No. Do it ...Acked-by: Rodolfo Giometti <giometti@linux.it> -- GNU/Linux Solutions e-mail: giometti@enneenne.com Linux Device Driver giometti@linux.it Embedded Systems phone: +39 349 2432127 UNIX programming skype: rodolfo.giometti Freelance ICT Italia - Consulente ICT Italia - www.consulenti-ict.it --
Here are some very trivial fixes combined:
* add macro definitions to protect header file from including several times
* remove declaration for an unexistent array
* fix typos
Signed-off-by: Alexander Gordeev <lasaine@lvk.cs.msu.su>
---
drivers/pps/kapi.c | 2 +-
include/linux/pps_kernel.h | 7 ++++++-
2 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/drivers/pps/kapi.c b/drivers/pps/kapi.c
index 1aa02db..55f3961 100644
--- a/drivers/pps/kapi.c
+++ b/drivers/pps/kapi.c
@@ -324,7 +324,7 @@ void pps_event(int source, struct pps_ktime *ts, int event, void *data)
captured = ~0;
}
- /* Wake up iif captured somthing */
+ /* Wake up if captured something */
if (captured) {
pps->go = ~0;
wake_up_interruptible(&pps->queue);
diff --git a/include/linux/pps_kernel.h b/include/linux/pps_kernel.h
index e0a193f..c930d11 100644
--- a/include/linux/pps_kernel.h
+++ b/include/linux/pps_kernel.h
@@ -18,6 +18,9 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#ifndef LINUX_PPS_KERNEL_H
+#define LINUX_PPS_KERNEL_H
+
#include <linux/pps.h>
#include <linux/cdev.h>
@@ -71,7 +74,6 @@ struct pps_device {
extern spinlock_t pps_idr_lock;
extern struct idr pps_idr;
-extern struct timespec pps_irq_ts[];
extern struct device_attribute pps_attrs[];
@@ -87,3 +89,6 @@ extern void pps_unregister_source(int source);
extern int pps_register_cdev(struct pps_device *pps);
extern void pps_unregister_cdev(struct pps_device *pps);
extern void pps_event(int source, struct pps_ktime *ts, int event, void *data);
+
+#endif /* LINUX_PPS_KERNEL_H */
+
--
1.7.1
--
Acked-by: Rodolfo Giometti <giometti@linux.it> -- GNU/Linux Solutions e-mail: giometti@enneenne.com Linux Device Driver giometti@linux.it Embedded Systems phone: +39 349 2432127 UNIX programming skype: rodolfo.giometti Freelance ICT Italia - Consulente ICT Italia - www.consulenti-ict.it --
There was a race in PPS_FETCH ioctl handler when several processes want
to obtain PPS data simultaneously using sleeping PPS_FETCH. They all
sleep most of the time in the system call.
With the old approach when the first process waiting on the pps queue
is waken up it makes new system call right away and zeroes pps->go. So
other processes continue to sleep. This is a clear race condition
because of the global 'go' variable.
With the new approach pps->last_ev holds some value increasing at each
PPS event. PPS_FETCH ioctl handler saves current value to the local
variable at the very beginning so it can safely check that there is a
new event by just comparing both variables.
Signed-off-by: Alexander Gordeev <lasaine@lvk.cs.msu.su>
---
drivers/pps/kapi.c | 4 ++--
drivers/pps/pps.c | 10 +++++++---
include/linux/pps_kernel.h | 2 +-
3 files changed, 10 insertions(+), 6 deletions(-)
diff --git a/drivers/pps/kapi.c b/drivers/pps/kapi.c
index 55f3961..3f89f5e 100644
--- a/drivers/pps/kapi.c
+++ b/drivers/pps/kapi.c
@@ -326,8 +326,8 @@ void pps_event(int source, struct pps_ktime *ts, int event, void *data)
/* Wake up if captured something */
if (captured) {
- pps->go = ~0;
- wake_up_interruptible(&pps->queue);
+ pps->last_ev++;
+ wake_up_interruptible_all(&pps->queue);
kill_fasync(&pps->async_queue, SIGIO, POLL_IN);
}
diff --git a/drivers/pps/pps.c b/drivers/pps/pps.c
index c76afb9..cb24147 100644
--- a/drivers/pps/pps.c
+++ b/drivers/pps/pps.c
@@ -136,6 +136,7 @@ static long pps_cdev_ioctl(struct file *file,
case PPS_FETCH: {
struct pps_fdata fdata;
+ unsigned int ev;
pr_debug("PPS_FETCH: source %d\n", pps->id);
@@ -143,11 +144,12 @@ static long pps_cdev_ioctl(struct file *file,
if (err)
return -EFAULT;
- pps->go = 0;
+ ev = pps->last_ev;
/* Manage the timeout */
if (fdata.timeout.flags & PPS_TIME_INVALID)
- err = wait_event_interruptible(pps->queue, pps->go);
+ err = ...What happens if pps->last_ev overflows? Seems to me it would freeze pps. Cheers, Vita --
Hi Vitezslav, В Thu, 5 Aug 2010 07:19:29 +0200 Yes, it will freeze the fds (if they don't use timeouts). But in normal circumstances, i.e. when pps_event is called twice a second, it will overflow after ~68 years of uninterrupted work. Well, it's the same kind of problem as an overflow of struct timespec. I thought it's not actually a problem. Should I use u64 instead of unsigned int or add a runtime check somewhere? -- Alexander
If we're using 1PPS it's ~68 years, but someone is trying 5PPS now (it would overflow in ~13.6 years) - what if someone tries e.g. 100PPS? It's not the same as overflow of struct timespec! I think it deserves some treatment. Cheers, Vita --
В Thu, 5 Aug 2010 13:07:50 +0200 You are right. :) I'll use u64 here then which should be enough for sure, ok? Thanks for the note! -- Alexander
Yes. It was added when we passed from ioctl to unlocked_ioctl... Acked-by: Rodolfo Giometti <giometti@linux.it> -- GNU/Linux Solutions e-mail: giometti@enneenne.com Linux Device Driver giometti@linux.it Embedded Systems phone: +39 349 2432127 UNIX programming skype: rodolfo.giometti Freelance ICT Italia - Consulente ICT Italia - www.consulenti-ict.it --
Bitwise conjunction is distributive so we can simplify some conditions.
Signed-off-by: Alexander Gordeev <lasaine@lvk.cs.msu.su>
---
drivers/pps/kapi.c | 6 ++----
1 files changed, 2 insertions(+), 4 deletions(-)
diff --git a/drivers/pps/kapi.c b/drivers/pps/kapi.c
index 220ab08..0335b2c 100644
--- a/drivers/pps/kapi.c
+++ b/drivers/pps/kapi.c
@@ -180,8 +180,7 @@ void pps_event(struct pps_device *pps, struct pps_event_time *ts, int event,
/* Check the event */
pps->current_mode = pps->params.mode;
- if ((event & PPS_CAPTUREASSERT) &
- (pps->params.mode & PPS_CAPTUREASSERT)) {
+ if (event & pps->params.mode & PPS_CAPTUREASSERT) {
/* We have to add an offset? */
if (pps->params.mode & PPS_OFFSETASSERT)
pps_add_offset(&ts_real,
@@ -195,8 +194,7 @@ void pps_event(struct pps_device *pps, struct pps_event_time *ts, int event,
captured = ~0;
}
- if ((event & PPS_CAPTURECLEAR) &
- (pps->params.mode & PPS_CAPTURECLEAR)) {
+ if (event & pps->params.mode & PPS_CAPTURECLEAR) {
/* We have to add an offset? */
if (pps->params.mode & PPS_OFFSETCLEAR)
pps_add_offset(&ts_real,
--
1.7.1
--
Since now idr is only used to manage char device id's and not used in
kernel API anymore it should be moved to pps.c. This also makes it
possible to release id only at actual device freeing so nobody can
register a pps device with the same id while our device is not freed
yet.
Signed-off-by: Alexander Gordeev <lasaine@lvk.cs.msu.su>
---
drivers/pps/kapi.c | 53 +---------------------------------------------------
drivers/pps/pps.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 50 insertions(+), 54 deletions(-)
diff --git a/drivers/pps/kapi.c b/drivers/pps/kapi.c
index eddf41e..041c27e 100644
--- a/drivers/pps/kapi.c
+++ b/drivers/pps/kapi.c
@@ -26,19 +26,11 @@
#include <linux/sched.h>
#include <linux/time.h>
#include <linux/spinlock.h>
-#include <linux/idr.h>
#include <linux/fs.h>
#include <linux/pps_kernel.h>
#include <linux/slab.h>
/*
- * Local variables
- */
-
-static DEFINE_SPINLOCK(pps_idr_lock);
-static DEFINE_IDR(pps_idr);
-
-/*
* Local functions
*/
@@ -75,7 +67,6 @@ struct pps_device *pps_register_source(struct pps_source_info *info,
int default_params)
{
struct pps_device *pps;
- int id;
int err;
/* Sanity checks */
@@ -116,54 +107,18 @@ struct pps_device *pps_register_source(struct pps_source_info *info,
init_waitqueue_head(&pps->queue);
spin_lock_init(&pps->lock);
- /* Get new ID for the new PPS source */
- if (idr_pre_get(&pps_idr, GFP_KERNEL) == 0) {
- err = -ENOMEM;
- goto kfree_pps;
- }
-
- spin_lock_irq(&pps_idr_lock);
-
- /* Now really allocate the PPS source.
- * After idr_get_new() calling the new source will be freely available
- * into the kernel.
- */
- err = idr_get_new(&pps_idr, pps, &id);
- if (err < 0) {
- spin_unlock_irq(&pps_idr_lock);
- goto kfree_pps;
- }
-
- id = id & MAX_ID_MASK;
- if (id >= PPS_MAX_SOURCES) {
- spin_unlock_irq(&pps_idr_lock);
-
- pr_err("pps: %s: too many PPS sources in the system\n",
- info->name);
- err = ...MONOTONIC_RAW clock timestamps are ideally suited for frequency
calculation and also fit well into the original NTP hardpps design. Now
phase and frequency can be adjusted separately: the former based on
REALTIME clock and the latter based on MONOTONIC_RAW clock.
A new function getnstime_raw_and_real is added to timekeeping subsystem
to capture both timestamps at the same time and atomically.
Signed-off-by: Alexander Gordeev <lasaine@lvk.cs.msu.su>
---
include/linux/pps_kernel.h | 3 ++-
include/linux/time.h | 2 ++
kernel/time/timekeeping.c | 34 ++++++++++++++++++++++++++++++++++
3 files changed, 38 insertions(+), 1 deletions(-)
diff --git a/include/linux/pps_kernel.h b/include/linux/pps_kernel.h
index 5af0498..39fc712 100644
--- a/include/linux/pps_kernel.h
+++ b/include/linux/pps_kernel.h
@@ -48,6 +48,7 @@ struct pps_source_info {
};
struct pps_event_time {
+ struct timespec ts_raw;
struct timespec ts_real;
};
@@ -111,7 +112,7 @@ static inline void timespec_to_pps_ktime(struct pps_ktime *kt,
static inline void pps_get_ts(struct pps_event_time *ts)
{
- getnstimeofday(&ts->ts_real);
+ getnstime_raw_and_real(&ts->ts_raw, &ts->ts_real);
}
#endif /* LINUX_PPS_KERNEL_H */
diff --git a/include/linux/time.h b/include/linux/time.h
index ea3559f..1da8a7b 100644
--- a/include/linux/time.h
+++ b/include/linux/time.h
@@ -143,6 +143,8 @@ extern unsigned int alarm_setitimer(unsigned int seconds);
extern int do_getitimer(int which, struct itimerval *value);
extern void getnstimeofday(struct timespec *tv);
extern void getrawmonotonic(struct timespec *ts);
+extern void getnstime_raw_and_real(struct timespec *ts_raw,
+ struct timespec *ts_real);
extern void getboottime(struct timespec *ts);
extern void monotonic_to_bootbased(struct timespec *ts);
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
index caf8d4d..ef37b14 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -286,6 +286,40 @@ void ...Hmm. So this brings up an interesting point. If the system is an one which uses arch_gettimeoffset() (which means it doesn't have clocksources), I'm not sure if this function would return what you'd expect. The issue is that jiffies is the basic clocksource that is in use on these systems, but the arch_gettimeoffset() function returns an inter-tick time value (usually by reading the interrupt generating decrementer or whatnot). So in this case, you'd get a CLOCK_MONOTONIC_RAW time that is based off the raw jiffies value at the last tick, and CLOCK_REALTIME time value at the exact time of the call. That offset between the two would then vary depending on when in the inter-tick period the call was made. The two solutions would be: 1) Add the arch_gettimeoffset() value to nsecs_raw as well. Should be safe, as any freq variance from the raw freq will be limited to 1 tick length and won't accumulate. 2) Don't add arch_gettimeoffset to either value, providing the raw and real time values at the last tick. I'd probably go with #1. Otherwise, the code looks good. thanks -john --
В Wed, 04 Aug 2010 16:03:57 -0700 Ok, thanks for the point! I'll use #1 then. -- Alexander
On Thu, 5 Aug 2010 01:06:50 +0400 I suspect that if this warning ever triggers, it'll trigger at some high frequency making a complete mess all over the floor. WARN_ON_ONCE, perhaps? Or just remove it? --
В Wed, 4 Aug 2010 16:29:07 -0700 Well, getnstime_raw_and_real() is actually just a merge of getnstimeofday() and getrawmonotonic(). The warning came from getnstimeofday(). Usually this code should be called once a second but the frequency can be higher. IMHO other functions like getnstimeofday() and ktime_get_ts() would be a bigger problem anyway because they have the same checks. So I'm ok with either choice. Hmm, will use WARN_ON_ONCE then if nobody objects. -- Alexander
This handler should be called from an IRQ handler. It uses per-device workqueue internally. Signed-off-by: Alexander Gordeev <lasaine@lvk.cs.msu.su> --- drivers/pps/clients/pps-ktimer.c | 2 +- drivers/pps/clients/pps-ldisc.c | 2 +- drivers/pps/kapi.c | 95 ++++++++++++++++++++++++++++++++++++-- drivers/pps/pps.c | 14 +++++- include/linux/pps_kernel.h | 16 ++++++- 5 files changed, 119 insertions(+), 10 deletions(-) diff --git a/drivers/pps/clients/pps-ktimer.c b/drivers/pps/clients/pps-ktimer.c index 9d27239..61d0800 100644 --- a/drivers/pps/clients/pps-ktimer.c +++ b/drivers/pps/clients/pps-ktimer.c @@ -47,7 +47,7 @@ static void pps_ktimer_event(unsigned long ptr) dev_info(pps->dev, "PPS event at %lu\n", jiffies); - pps_event(pps, &ts, PPS_CAPTUREASSERT, NULL); + pps_event_irq(pps, &ts, PPS_CAPTUREASSERT, NULL); mod_timer(&ktimer, jiffies + HZ); } diff --git a/drivers/pps/clients/pps-ldisc.c b/drivers/pps/clients/pps-ldisc.c index d7e1a27..3e64042 100644 --- a/drivers/pps/clients/pps-ldisc.c +++ b/drivers/pps/clients/pps-ldisc.c @@ -40,7 +40,7 @@ static void pps_tty_dcd_change(struct tty_struct *tty, unsigned int status, ts = &__ts; /* Now do the PPS event report */ - pps_event(pps, ts, status ? PPS_CAPTUREASSERT : PPS_CAPTURECLEAR, + pps_event_irq(pps, ts, status ? PPS_CAPTUREASSERT : PPS_CAPTURECLEAR, NULL); dev_dbg(pps->dev, "PPS %s at %lu\n", status ? "assert" : "clear", diff --git a/drivers/pps/kapi.c b/drivers/pps/kapi.c index 041c27e..d5f18ce 100644 --- a/drivers/pps/kapi.c +++ b/drivers/pps/kapi.c @@ -31,9 +31,19 @@ #include <linux/slab.h> /* + * Global variables + */ + +/* PPS event workqueue */ +struct workqueue_struct *pps_event_workqueue; + +/* * Local functions */ +static void assert_work_func(struct work_struct *work); +static void clear_work_func(struct work_struct *work); + static void pps_add_offset(struct pps_ktime *ts, struct pps_ktime ...
Add an optional feature of PPSAPI, kernel consumer support, which uses the added hardpps() function. Signed-off-by: Alexander Gordeev <lasaine@lvk.cs.msu.su> --- Documentation/ioctl/ioctl-number.txt | 2 +- drivers/pps/Kconfig | 1 + drivers/pps/kapi.c | 23 ++++++++++++ drivers/pps/pps.c | 62 +++++++++++++++++++++++++++++++++- include/linux/pps.h | 7 ++++ include/linux/pps_kernel.h | 6 +++ 6 files changed, 99 insertions(+), 2 deletions(-) diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt index dd5806f..e507f84 100644 --- a/Documentation/ioctl/ioctl-number.txt +++ b/Documentation/ioctl/ioctl-number.txt @@ -248,7 +248,7 @@ Code Seq#(hex) Include File Comments 'p' 40-7F linux/nvram.h 'p' 80-9F linux/ppdev.h user-space parport <mailto:tim@cyberelk.net> -'p' A1-A4 linux/pps.h LinuxPPS +'p' A1-A5 linux/pps.h LinuxPPS <mailto:giometti@linux.it> 'q' 00-1F linux/serio.h 'q' 80-FF linux/telephony.h Internet PhoneJACK, Internet LineJACK diff --git a/drivers/pps/Kconfig b/drivers/pps/Kconfig index 33c9126..4823e47 100644 --- a/drivers/pps/Kconfig +++ b/drivers/pps/Kconfig @@ -7,6 +7,7 @@ menu "PPS support" config PPS tristate "PPS support" depends on EXPERIMENTAL + select NTP_PPS ---help--- PPS (Pulse Per Second) is a special pulse provided by some GPS antennae. Userland can use it to get a high-precision time diff --git a/drivers/pps/kapi.c b/drivers/pps/kapi.c index 0335b2c..02f4dd1 100644 --- a/drivers/pps/kapi.c +++ b/drivers/pps/kapi.c @@ -25,6 +25,7 @@ #include <linux/init.h> #include <linux/sched.h> #include <linux/time.h> +#include <linux/timex.h> #include <linux/spinlock.h> #include <linux/fs.h> #include <linux/pps_kernel.h> @@ -37,6 +38,12 @@ /* PPS event workqueue */ struct workqueue_struct *pps_event_workqueue; +/* state variables to bind kernel consumer */ +/* PPS API ...
This way less overhead is involved when running production kernel.
If you want to debug a pps client module please define DEBUG to enable
the checks.
Signed-off-by: Alexander Gordeev <lasaine@lvk.cs.msu.su>
---
drivers/pps/kapi.c | 33 ++++++++++-----------------------
1 files changed, 10 insertions(+), 23 deletions(-)
diff --git a/drivers/pps/kapi.c b/drivers/pps/kapi.c
index 8c557c4..220ab08 100644
--- a/drivers/pps/kapi.c
+++ b/drivers/pps/kapi.c
@@ -80,25 +80,14 @@ struct pps_device *pps_register_source(struct pps_source_info *info,
int err;
/* Sanity checks */
- if ((info->mode & default_params) != default_params) {
- pr_err("pps: %s: unsupported default parameters\n",
- info->name);
- err = -EINVAL;
- goto pps_register_source_exit;
- }
- if ((info->mode & (PPS_ECHOASSERT | PPS_ECHOCLEAR)) != 0 &&
- info->echo == NULL) {
- pr_err("pps: %s: echo function is not defined\n",
- info->name);
- err = -EINVAL;
- goto pps_register_source_exit;
- }
- if ((info->mode & (PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP)) == 0) {
- pr_err("pps: %s: unspecified time format\n",
- info->name);
- err = -EINVAL;
- goto pps_register_source_exit;
- }
+
+ /* default_params should be supported */
+ BUG_ON((info->mode & default_params) != default_params);
+ /* echo function should be defined if we are asked to call it */
+ BUG_ON((info->mode & (PPS_ECHOASSERT | PPS_ECHOCLEAR)) != 0 &&
+ info->echo == NULL);
+ /* time format should be specified */
+ BUG_ON((info->mode & (PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP)) == 0);
/* Allocate memory for the new PPS source struct */
pps = kzalloc(sizeof(struct pps_device), GFP_KERNEL);
@@ -175,10 +164,8 @@ void pps_event(struct pps_device *pps, struct pps_event_time *ts, int event,
int captured = 0;
struct pps_ktime ts_real;
- if ((event & (PPS_CAPTUREASSERT | PPS_CAPTURECLEAR)) == 0) {
- dev_err(pps->dev, "unknown event (%x)\n", event);
- return;
- }
+ /* check event type */
+ BUG_ON((event & ...Now all PPS spin locks are never used in interrupt context so we can
safely replace spin_lock_irq*/spin_unlock_irq* with plain
spin_lock/spin_unlock.
Signed-off-by: Alexander Gordeev <lasaine@lvk.cs.msu.su>
---
drivers/pps/kapi.c | 5 ++---
drivers/pps/pps.c | 24 ++++++++++++------------
2 files changed, 14 insertions(+), 15 deletions(-)
diff --git a/drivers/pps/kapi.c b/drivers/pps/kapi.c
index d5f18ce..8c557c4 100644
--- a/drivers/pps/kapi.c
+++ b/drivers/pps/kapi.c
@@ -172,7 +172,6 @@ EXPORT_SYMBOL(pps_unregister_source);
void pps_event(struct pps_device *pps, struct pps_event_time *ts, int event,
void *data)
{
- unsigned long flags;
int captured = 0;
struct pps_ktime ts_real;
@@ -186,7 +185,7 @@ void pps_event(struct pps_device *pps, struct pps_event_time *ts, int event,
timespec_to_pps_ktime(&ts_real, ts->ts_real);
- spin_lock_irqsave(&pps->lock, flags);
+ spin_lock(&pps->lock);
/* Must call the echo function? */
if ((pps->params.mode & (PPS_ECHOASSERT | PPS_ECHOCLEAR)))
@@ -233,7 +232,7 @@ void pps_event(struct pps_device *pps, struct pps_event_time *ts, int event,
kill_fasync(&pps->async_queue, SIGIO, POLL_IN);
}
- spin_unlock_irqrestore(&pps->lock, flags);
+ spin_unlock(&pps->lock);
}
EXPORT_SYMBOL(pps_event);
diff --git a/drivers/pps/pps.c b/drivers/pps/pps.c
index 81adb33..4f02718 100644
--- a/drivers/pps/pps.c
+++ b/drivers/pps/pps.c
@@ -73,12 +73,12 @@ static long pps_cdev_ioctl(struct file *file,
case PPS_GETPARAMS:
dev_dbg(pps->dev, "PPS_GETPARAMS\n");
- spin_lock_irq(&pps->lock);
+ spin_lock(&pps->lock);
/* Get the current parameters */
params = pps->params;
- spin_unlock_irq(&pps->lock);
+ spin_unlock(&pps->lock);
err = copy_to_user(uarg, &params, sizeof(struct pps_kparams));
if (err)
@@ -109,7 +109,7 @@ static long pps_cdev_ioctl(struct file *file,
return -EINVAL;
}
- spin_lock_irq(&pps->lock);
+ spin_lock(&pps->lock);
/* Save the new ...Add parallel port PPS client. It uses a standard method for capturing timestamps for assert edge transitions: getting a timestamp soon after an interrupt has happened. This is not a very precise source of time information due to interrupt handling delays. However, timestamps for clear edge transitions are much more precise because the interrupt handler continuously polls hardware port until the transition is done. Hardware port operations require only about 1us so the maximum error should not exceed this value. This was my primary goal when developing this client. Clear edge capture could be disabled using clear_wait parameter. Signed-off-by: Alexander Gordeev <lasaine@lvk.cs.msu.su> --- drivers/pps/clients/Kconfig | 7 + drivers/pps/clients/Makefile | 1 + drivers/pps/clients/pps_parport.c | 244 +++++++++++++++++++++++++++++++++++++ 3 files changed, 252 insertions(+), 0 deletions(-) create mode 100644 drivers/pps/clients/pps_parport.c diff --git a/drivers/pps/clients/Kconfig b/drivers/pps/clients/Kconfig index 4e801bd..8520a7f 100644 --- a/drivers/pps/clients/Kconfig +++ b/drivers/pps/clients/Kconfig @@ -22,4 +22,11 @@ config PPS_CLIENT_LDISC If you say yes here you get support for a PPS source connected with the CD (Carrier Detect) pin of your serial port. +config PPS_CLIENT_PARPORT + tristate "Parallel port PPS client" + depends on PPS && PARPORT + help + If you say yes here you get support for a PPS source connected + with the interrupt pin of your parallel port. + endif diff --git a/drivers/pps/clients/Makefile b/drivers/pps/clients/Makefile index 812c9b1..42517da 100644 --- a/drivers/pps/clients/Makefile +++ b/drivers/pps/clients/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_PPS_CLIENT_KTIMER) += pps-ktimer.o obj-$(CONFIG_PPS_CLIENT_LDISC) += pps-ldisc.o +obj-$(CONFIG_PPS_CLIENT_PARPORT) += pps_parport.o ifeq ($(CONFIG_PPS_DEBUG),y) EXTRA_CFLAGS += -DDEBUG diff --git a/drivers/pps/clients/pps_parport.c ...
On Thu, 5 Aug 2010 01:06:52 +0400 This could (and hence should) be implemented in a regular old C eww. parport_driver.attach() returns void so there's no way to --
В Wed, 4 Aug 2010 16:34:30 -0700 IMHO, the whole parport thing currently sucks quite a bit. -- Alexander
Add PPS signal generator which utilizes STROBE pin of a parallel port to send PPS signals. It uses parport abstraction layer and hrtimers to precisely control the signal. Signed-off-by: Alexander Gordeev <lasaine@lvk.cs.msu.su> --- drivers/pps/Kconfig | 2 + drivers/pps/Makefile | 2 +- drivers/pps/generators/Kconfig | 17 ++ drivers/pps/generators/Makefile | 9 + drivers/pps/generators/pps_gen_parport.c | 274 ++++++++++++++++++++++++++++++ 5 files changed, 303 insertions(+), 1 deletions(-) create mode 100644 drivers/pps/generators/Kconfig create mode 100644 drivers/pps/generators/Makefile create mode 100644 drivers/pps/generators/pps_gen_parport.c diff --git a/drivers/pps/Kconfig b/drivers/pps/Kconfig index 4823e47..79bda60 100644 --- a/drivers/pps/Kconfig +++ b/drivers/pps/Kconfig @@ -40,4 +40,6 @@ config NTP_PPS source drivers/pps/clients/Kconfig +source drivers/pps/generators/Kconfig + endmenu diff --git a/drivers/pps/Makefile b/drivers/pps/Makefile index 98960dd..1906eb7 100644 --- a/drivers/pps/Makefile +++ b/drivers/pps/Makefile @@ -4,6 +4,6 @@ pps_core-y := pps.o kapi.o sysfs.o obj-$(CONFIG_PPS) := pps_core.o -obj-y += clients/ +obj-y += clients/ generators/ ccflags-$(CONFIG_PPS_DEBUG) := -DDEBUG diff --git a/drivers/pps/generators/Kconfig b/drivers/pps/generators/Kconfig new file mode 100644 index 0000000..5fbd614 --- /dev/null +++ b/drivers/pps/generators/Kconfig @@ -0,0 +1,17 @@ +# +# PPS generators configuration +# + +if PPS + +comment "PPS generators support" + +config PPS_GENERATOR_PARPORT + tristate "Parallel port PPS signal generator" + depends on PARPORT != n && GENERIC_TIME + help + If you say yes here you get support for a PPS signal generator which + utilizes STROBE pin of a parallel port to send PPS signals. It uses + parport abstraction layer and hrtimers to precisely control the signal. + +endif diff --git ...
On Thu, 5 Aug 2010 01:06:53 +0400 This code doesn't look easy to configure and use. For example, if some random person tries to get it going, how does he even know that this module parameter exists, let alone how to use it? Please review Documentation/pps/pps.txt and check that it accurately and completely describes how to use the pps code after your changes, thanks. --
