diff options
author | Alexander Gordeev <lasaine@lvk.cs.msu.su> | 2011-01-12 20:00:58 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-01-13 11:03:21 -0500 |
commit | 717c033669ed3ceaee8df57d4562fafcc1a6267a (patch) | |
tree | 892e922c08b5f5eee8fbe7c8e0fdc774db660c67 /drivers/pps | |
parent | e2c18e49a0d4f822ffc29fb4958943beb1ff08b7 (diff) |
pps: add kernel consumer support
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>
Acked-by: Rodolfo Giometti <giometti@linux.it>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/pps')
-rw-r--r-- | drivers/pps/Makefile | 1 | ||||
-rw-r--r-- | drivers/pps/kapi.c | 6 | ||||
-rw-r--r-- | drivers/pps/kc.c | 122 | ||||
-rw-r--r-- | drivers/pps/kc.h | 46 | ||||
-rw-r--r-- | drivers/pps/pps.c | 38 |
5 files changed, 212 insertions, 1 deletions
diff --git a/drivers/pps/Makefile b/drivers/pps/Makefile index 98960ddd3188..77c23457b739 100644 --- a/drivers/pps/Makefile +++ b/drivers/pps/Makefile | |||
@@ -3,6 +3,7 @@ | |||
3 | # | 3 | # |
4 | 4 | ||
5 | pps_core-y := pps.o kapi.o sysfs.o | 5 | pps_core-y := pps.o kapi.o sysfs.o |
6 | pps_core-$(CONFIG_NTP_PPS) += kc.o | ||
6 | obj-$(CONFIG_PPS) := pps_core.o | 7 | obj-$(CONFIG_PPS) := pps_core.o |
7 | obj-y += clients/ | 8 | obj-y += clients/ |
8 | 9 | ||
diff --git a/drivers/pps/kapi.c b/drivers/pps/kapi.c index ebf33746c37c..cba1b43f7519 100644 --- a/drivers/pps/kapi.c +++ b/drivers/pps/kapi.c | |||
@@ -26,11 +26,14 @@ | |||
26 | #include <linux/init.h> | 26 | #include <linux/init.h> |
27 | #include <linux/sched.h> | 27 | #include <linux/sched.h> |
28 | #include <linux/time.h> | 28 | #include <linux/time.h> |
29 | #include <linux/timex.h> | ||
29 | #include <linux/spinlock.h> | 30 | #include <linux/spinlock.h> |
30 | #include <linux/fs.h> | 31 | #include <linux/fs.h> |
31 | #include <linux/pps_kernel.h> | 32 | #include <linux/pps_kernel.h> |
32 | #include <linux/slab.h> | 33 | #include <linux/slab.h> |
33 | 34 | ||
35 | #include "kc.h" | ||
36 | |||
34 | /* | 37 | /* |
35 | * Local functions | 38 | * Local functions |
36 | */ | 39 | */ |
@@ -139,6 +142,7 @@ EXPORT_SYMBOL(pps_register_source); | |||
139 | 142 | ||
140 | void pps_unregister_source(struct pps_device *pps) | 143 | void pps_unregister_source(struct pps_device *pps) |
141 | { | 144 | { |
145 | pps_kc_remove(pps); | ||
142 | pps_unregister_cdev(pps); | 146 | pps_unregister_cdev(pps); |
143 | 147 | ||
144 | /* don't have to kfree(pps) here because it will be done on | 148 | /* don't have to kfree(pps) here because it will be done on |
@@ -211,6 +215,8 @@ void pps_event(struct pps_device *pps, struct pps_event_time *ts, int event, | |||
211 | captured = ~0; | 215 | captured = ~0; |
212 | } | 216 | } |
213 | 217 | ||
218 | pps_kc_event(pps, ts, event); | ||
219 | |||
214 | /* Wake up if captured something */ | 220 | /* Wake up if captured something */ |
215 | if (captured) { | 221 | if (captured) { |
216 | pps->last_ev++; | 222 | pps->last_ev++; |
diff --git a/drivers/pps/kc.c b/drivers/pps/kc.c new file mode 100644 index 000000000000..079e930b1938 --- /dev/null +++ b/drivers/pps/kc.c | |||
@@ -0,0 +1,122 @@ | |||
1 | /* | ||
2 | * PPS kernel consumer API | ||
3 | * | ||
4 | * Copyright (C) 2009-2010 Alexander Gordeev <lasaine@lvk.cs.msu.su> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
19 | */ | ||
20 | |||
21 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
22 | |||
23 | #include <linux/kernel.h> | ||
24 | #include <linux/module.h> | ||
25 | #include <linux/device.h> | ||
26 | #include <linux/init.h> | ||
27 | #include <linux/spinlock.h> | ||
28 | #include <linux/pps_kernel.h> | ||
29 | |||
30 | #include "kc.h" | ||
31 | |||
32 | /* | ||
33 | * Global variables | ||
34 | */ | ||
35 | |||
36 | /* state variables to bind kernel consumer */ | ||
37 | DEFINE_SPINLOCK(pps_kc_hardpps_lock); | ||
38 | /* PPS API (RFC 2783): current source and mode for kernel consumer */ | ||
39 | struct pps_device *pps_kc_hardpps_dev; /* unique pointer to device */ | ||
40 | int pps_kc_hardpps_mode; /* mode bits for kernel consumer */ | ||
41 | |||
42 | /* pps_kc_bind - control PPS kernel consumer binding | ||
43 | * @pps: the PPS source | ||
44 | * @bind_args: kernel consumer bind parameters | ||
45 | * | ||
46 | * This function is used to bind or unbind PPS kernel consumer according to | ||
47 | * supplied parameters. Should not be called in interrupt context. | ||
48 | */ | ||
49 | int pps_kc_bind(struct pps_device *pps, struct pps_bind_args *bind_args) | ||
50 | { | ||
51 | /* Check if another consumer is already bound */ | ||
52 | spin_lock_irq(&pps_kc_hardpps_lock); | ||
53 | |||
54 | if (bind_args->edge == 0) | ||
55 | if (pps_kc_hardpps_dev == pps) { | ||
56 | pps_kc_hardpps_mode = 0; | ||
57 | pps_kc_hardpps_dev = NULL; | ||
58 | spin_unlock_irq(&pps_kc_hardpps_lock); | ||
59 | dev_info(pps->dev, "unbound kernel" | ||
60 | " consumer\n"); | ||
61 | } else { | ||
62 | spin_unlock_irq(&pps_kc_hardpps_lock); | ||
63 | dev_err(pps->dev, "selected kernel consumer" | ||
64 | " is not bound\n"); | ||
65 | return -EINVAL; | ||
66 | } | ||
67 | else | ||
68 | if (pps_kc_hardpps_dev == NULL || | ||
69 | pps_kc_hardpps_dev == pps) { | ||
70 | pps_kc_hardpps_mode = bind_args->edge; | ||
71 | pps_kc_hardpps_dev = pps; | ||
72 | spin_unlock_irq(&pps_kc_hardpps_lock); | ||
73 | dev_info(pps->dev, "bound kernel consumer: " | ||
74 | "edge=0x%x\n", bind_args->edge); | ||
75 | } else { | ||
76 | spin_unlock_irq(&pps_kc_hardpps_lock); | ||
77 | dev_err(pps->dev, "another kernel consumer" | ||
78 | " is already bound\n"); | ||
79 | return -EINVAL; | ||
80 | } | ||
81 | |||
82 | return 0; | ||
83 | } | ||
84 | |||
85 | /* pps_kc_remove - unbind kernel consumer on PPS source removal | ||
86 | * @pps: the PPS source | ||
87 | * | ||
88 | * This function is used to disable kernel consumer on PPS source removal | ||
89 | * if this source was bound to PPS kernel consumer. Can be called on any | ||
90 | * source safely. Should not be called in interrupt context. | ||
91 | */ | ||
92 | void pps_kc_remove(struct pps_device *pps) | ||
93 | { | ||
94 | spin_lock_irq(&pps_kc_hardpps_lock); | ||
95 | if (pps == pps_kc_hardpps_dev) { | ||
96 | pps_kc_hardpps_mode = 0; | ||
97 | pps_kc_hardpps_dev = NULL; | ||
98 | spin_unlock_irq(&pps_kc_hardpps_lock); | ||
99 | dev_info(pps->dev, "unbound kernel consumer" | ||
100 | " on device removal\n"); | ||
101 | } else | ||
102 | spin_unlock_irq(&pps_kc_hardpps_lock); | ||
103 | } | ||
104 | |||
105 | /* pps_kc_event - call hardpps() on PPS event | ||
106 | * @pps: the PPS source | ||
107 | * @ts: PPS event timestamp | ||
108 | * @event: PPS event edge | ||
109 | * | ||
110 | * This function calls hardpps() when an event from bound PPS source occurs. | ||
111 | */ | ||
112 | void pps_kc_event(struct pps_device *pps, struct pps_event_time *ts, | ||
113 | int event) | ||
114 | { | ||
115 | unsigned long flags; | ||
116 | |||
117 | /* Pass some events to kernel consumer if activated */ | ||
118 | spin_lock_irqsave(&pps_kc_hardpps_lock, flags); | ||
119 | if (pps == pps_kc_hardpps_dev && event & pps_kc_hardpps_mode) | ||
120 | hardpps(&ts->ts_real, &ts->ts_raw); | ||
121 | spin_unlock_irqrestore(&pps_kc_hardpps_lock, flags); | ||
122 | } | ||
diff --git a/drivers/pps/kc.h b/drivers/pps/kc.h new file mode 100644 index 000000000000..d296fcd0a175 --- /dev/null +++ b/drivers/pps/kc.h | |||
@@ -0,0 +1,46 @@ | |||
1 | /* | ||
2 | * PPS kernel consumer API header | ||
3 | * | ||
4 | * Copyright (C) 2009-2010 Alexander Gordeev <lasaine@lvk.cs.msu.su> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
19 | */ | ||
20 | |||
21 | #ifndef LINUX_PPS_KC_H | ||
22 | #define LINUX_PPS_KC_H | ||
23 | |||
24 | #include <linux/errno.h> | ||
25 | #include <linux/pps_kernel.h> | ||
26 | |||
27 | #ifdef CONFIG_NTP_PPS | ||
28 | |||
29 | extern int pps_kc_bind(struct pps_device *pps, | ||
30 | struct pps_bind_args *bind_args); | ||
31 | extern void pps_kc_remove(struct pps_device *pps); | ||
32 | extern void pps_kc_event(struct pps_device *pps, | ||
33 | struct pps_event_time *ts, int event); | ||
34 | |||
35 | |||
36 | #else /* CONFIG_NTP_PPS */ | ||
37 | |||
38 | static inline int pps_kc_bind(struct pps_device *pps, | ||
39 | struct pps_bind_args *bind_args) { return -EOPNOTSUPP; } | ||
40 | static inline void pps_kc_remove(struct pps_device *pps) {} | ||
41 | static inline void pps_kc_event(struct pps_device *pps, | ||
42 | struct pps_event_time *ts, int event) {} | ||
43 | |||
44 | #endif /* CONFIG_NTP_PPS */ | ||
45 | |||
46 | #endif /* LINUX_PPS_KC_H */ | ||
diff --git a/drivers/pps/pps.c b/drivers/pps/pps.c index 9e15cf1da946..2baadd21b7a6 100644 --- a/drivers/pps/pps.c +++ b/drivers/pps/pps.c | |||
@@ -33,6 +33,8 @@ | |||
33 | #include <linux/pps_kernel.h> | 33 | #include <linux/pps_kernel.h> |
34 | #include <linux/slab.h> | 34 | #include <linux/slab.h> |
35 | 35 | ||
36 | #include "kc.h" | ||
37 | |||
36 | /* | 38 | /* |
37 | * Local variables | 39 | * Local variables |
38 | */ | 40 | */ |
@@ -198,9 +200,43 @@ static long pps_cdev_ioctl(struct file *file, | |||
198 | 200 | ||
199 | break; | 201 | break; |
200 | } | 202 | } |
203 | case PPS_KC_BIND: { | ||
204 | struct pps_bind_args bind_args; | ||
205 | |||
206 | dev_dbg(pps->dev, "PPS_KC_BIND\n"); | ||
207 | |||
208 | /* Check the capabilities */ | ||
209 | if (!capable(CAP_SYS_TIME)) | ||
210 | return -EPERM; | ||
211 | |||
212 | if (copy_from_user(&bind_args, uarg, | ||
213 | sizeof(struct pps_bind_args))) | ||
214 | return -EFAULT; | ||
215 | |||
216 | /* Check for supported capabilities */ | ||
217 | if ((bind_args.edge & ~pps->info.mode) != 0) { | ||
218 | dev_err(pps->dev, "unsupported capabilities (%x)\n", | ||
219 | bind_args.edge); | ||
220 | return -EINVAL; | ||
221 | } | ||
222 | |||
223 | /* Validate parameters roughly */ | ||
224 | if (bind_args.tsformat != PPS_TSFMT_TSPEC || | ||
225 | (bind_args.edge & ~PPS_CAPTUREBOTH) != 0 || | ||
226 | bind_args.consumer != PPS_KC_HARDPPS) { | ||
227 | dev_err(pps->dev, "invalid kernel consumer bind" | ||
228 | " parameters (%x)\n", bind_args.edge); | ||
229 | return -EINVAL; | ||
230 | } | ||
231 | |||
232 | err = pps_kc_bind(pps, &bind_args); | ||
233 | if (err < 0) | ||
234 | return err; | ||
235 | |||
236 | break; | ||
237 | } | ||
201 | default: | 238 | default: |
202 | return -ENOTTY; | 239 | return -ENOTTY; |
203 | break; | ||
204 | } | 240 | } |
205 | 241 | ||
206 | return 0; | 242 | return 0; |