aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexander Gordeev <lasaine@lvk.cs.msu.su>2011-01-12 20:00:58 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2011-01-13 11:03:21 -0500
commit717c033669ed3ceaee8df57d4562fafcc1a6267a (patch)
tree892e922c08b5f5eee8fbe7c8e0fdc774db660c67
parente2c18e49a0d4f822ffc29fb4958943beb1ff08b7 (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>
-rw-r--r--Documentation/ioctl/ioctl-number.txt2
-rw-r--r--drivers/pps/Makefile1
-rw-r--r--drivers/pps/kapi.c6
-rw-r--r--drivers/pps/kc.c122
-rw-r--r--drivers/pps/kc.h46
-rw-r--r--drivers/pps/pps.c38
-rw-r--r--include/linux/pps.h7
7 files changed, 220 insertions, 2 deletions
diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt
index d6a63c7b4478..ac293e955308 100644
--- a/Documentation/ioctl/ioctl-number.txt
+++ b/Documentation/ioctl/ioctl-number.txt
@@ -247,7 +247,7 @@ Code Seq#(hex) Include File Comments
247'p' 40-7F linux/nvram.h 247'p' 40-7F linux/nvram.h
248'p' 80-9F linux/ppdev.h user-space parport 248'p' 80-9F linux/ppdev.h user-space parport
249 <mailto:tim@cyberelk.net> 249 <mailto:tim@cyberelk.net>
250'p' A1-A4 linux/pps.h LinuxPPS 250'p' A1-A5 linux/pps.h LinuxPPS
251 <mailto:giometti@linux.it> 251 <mailto:giometti@linux.it>
252'q' 00-1F linux/serio.h 252'q' 00-1F linux/serio.h
253'q' 80-FF linux/telephony.h Internet PhoneJACK, Internet LineJACK 253'q' 80-FF linux/telephony.h Internet PhoneJACK, Internet LineJACK
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
5pps_core-y := pps.o kapi.o sysfs.o 5pps_core-y := pps.o kapi.o sysfs.o
6pps_core-$(CONFIG_NTP_PPS) += kc.o
6obj-$(CONFIG_PPS) := pps_core.o 7obj-$(CONFIG_PPS) := pps_core.o
7obj-y += clients/ 8obj-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
140void pps_unregister_source(struct pps_device *pps) 143void 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 */
37DEFINE_SPINLOCK(pps_kc_hardpps_lock);
38/* PPS API (RFC 2783): current source and mode for kernel consumer */
39struct pps_device *pps_kc_hardpps_dev; /* unique pointer to device */
40int 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 */
49int 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 */
92void 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 */
112void 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
29extern int pps_kc_bind(struct pps_device *pps,
30 struct pps_bind_args *bind_args);
31extern void pps_kc_remove(struct pps_device *pps);
32extern void pps_kc_event(struct pps_device *pps,
33 struct pps_event_time *ts, int event);
34
35
36#else /* CONFIG_NTP_PPS */
37
38static inline int pps_kc_bind(struct pps_device *pps,
39 struct pps_bind_args *bind_args) { return -EOPNOTSUPP; }
40static inline void pps_kc_remove(struct pps_device *pps) {}
41static 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;
diff --git a/include/linux/pps.h b/include/linux/pps.h
index 0194ab06177b..a9bb1d93451a 100644
--- a/include/linux/pps.h
+++ b/include/linux/pps.h
@@ -114,11 +114,18 @@ struct pps_fdata {
114 struct pps_ktime timeout; 114 struct pps_ktime timeout;
115}; 115};
116 116
117struct pps_bind_args {
118 int tsformat; /* format of time stamps */
119 int edge; /* selected event type */
120 int consumer; /* selected kernel consumer */
121};
122
117#include <linux/ioctl.h> 123#include <linux/ioctl.h>
118 124
119#define PPS_GETPARAMS _IOR('p', 0xa1, struct pps_kparams *) 125#define PPS_GETPARAMS _IOR('p', 0xa1, struct pps_kparams *)
120#define PPS_SETPARAMS _IOW('p', 0xa2, struct pps_kparams *) 126#define PPS_SETPARAMS _IOW('p', 0xa2, struct pps_kparams *)
121#define PPS_GETCAP _IOR('p', 0xa3, int *) 127#define PPS_GETCAP _IOR('p', 0xa3, int *)
122#define PPS_FETCH _IOWR('p', 0xa4, struct pps_fdata *) 128#define PPS_FETCH _IOWR('p', 0xa4, struct pps_fdata *)
129#define PPS_KC_BIND _IOW('p', 0xa5, struct pps_bind_args *)
123 130
124#endif /* _PPS_H_ */ 131#endif /* _PPS_H_ */