diff options
author | Glenn Elliott <gelliott@cs.unc.edu> | 2012-03-04 19:47:13 -0500 |
---|---|---|
committer | Glenn Elliott <gelliott@cs.unc.edu> | 2012-03-04 19:47:13 -0500 |
commit | c71c03bda1e86c9d5198c5d83f712e695c4f2a1e (patch) | |
tree | ecb166cb3e2b7e2adb3b5e292245fefd23381ac8 /drivers/pps | |
parent | ea53c912f8a86a8567697115b6a0d8152beee5c8 (diff) | |
parent | 6a00f206debf8a5c8899055726ad127dbeeed098 (diff) |
Merge branch 'mpi-master' into wip-k-fmlpwip-k-fmlp
Conflicts:
litmus/sched_cedf.c
Diffstat (limited to 'drivers/pps')
-rw-r--r-- | drivers/pps/Kconfig | 11 | ||||
-rw-r--r-- | drivers/pps/Makefile | 3 | ||||
-rw-r--r-- | drivers/pps/clients/Kconfig | 7 | ||||
-rw-r--r-- | drivers/pps/clients/Makefile | 5 | ||||
-rw-r--r-- | drivers/pps/clients/pps-ktimer.c | 44 | ||||
-rw-r--r-- | drivers/pps/clients/pps-ldisc.c | 59 | ||||
-rw-r--r-- | drivers/pps/clients/pps_parport.c | 258 | ||||
-rw-r--r-- | drivers/pps/generators/Kconfig | 13 | ||||
-rw-r--r-- | drivers/pps/generators/Makefile | 9 | ||||
-rw-r--r-- | drivers/pps/generators/pps_gen_parport.c | 277 | ||||
-rw-r--r-- | drivers/pps/kapi.c | 210 | ||||
-rw-r--r-- | drivers/pps/kc.c | 122 | ||||
-rw-r--r-- | drivers/pps/kc.h | 46 | ||||
-rw-r--r-- | drivers/pps/pps.c | 156 |
14 files changed, 963 insertions, 257 deletions
diff --git a/drivers/pps/Kconfig b/drivers/pps/Kconfig index 1afe4e03440f..258ca596e1bc 100644 --- a/drivers/pps/Kconfig +++ b/drivers/pps/Kconfig | |||
@@ -30,6 +30,17 @@ config PPS_DEBUG | |||
30 | messages to the system log. Select this if you are having a | 30 | messages to the system log. Select this if you are having a |
31 | problem with PPS support and want to see more of what is going on. | 31 | problem with PPS support and want to see more of what is going on. |
32 | 32 | ||
33 | config NTP_PPS | ||
34 | bool "PPS kernel consumer support" | ||
35 | depends on PPS && !NO_HZ | ||
36 | help | ||
37 | This option adds support for direct in-kernel time | ||
38 | synchronization using an external PPS signal. | ||
39 | |||
40 | It doesn't work on tickless systems at the moment. | ||
41 | |||
33 | source drivers/pps/clients/Kconfig | 42 | source drivers/pps/clients/Kconfig |
34 | 43 | ||
44 | source drivers/pps/generators/Kconfig | ||
45 | |||
35 | endmenu | 46 | endmenu |
diff --git a/drivers/pps/Makefile b/drivers/pps/Makefile index 98960ddd3188..4483eaadaddd 100644 --- a/drivers/pps/Makefile +++ b/drivers/pps/Makefile | |||
@@ -3,7 +3,8 @@ | |||
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/ generators/ |
8 | 9 | ||
9 | ccflags-$(CONFIG_PPS_DEBUG) := -DDEBUG | 10 | ccflags-$(CONFIG_PPS_DEBUG) := -DDEBUG |
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 | ||
25 | config 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 | |||
25 | endif | 32 | endif |
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 | ||
5 | obj-$(CONFIG_PPS_CLIENT_KTIMER) += pps-ktimer.o | 5 | obj-$(CONFIG_PPS_CLIENT_KTIMER) += pps-ktimer.o |
6 | obj-$(CONFIG_PPS_CLIENT_LDISC) += pps-ldisc.o | 6 | obj-$(CONFIG_PPS_CLIENT_LDISC) += pps-ldisc.o |
7 | obj-$(CONFIG_PPS_CLIENT_PARPORT) += pps_parport.o | ||
7 | 8 | ||
8 | ifeq ($(CONFIG_PPS_DEBUG),y) | 9 | ccflags-$(CONFIG_PPS_DEBUG) := -DDEBUG |
9 | EXTRA_CFLAGS += -DDEBUG | ||
10 | endif | ||
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 | ||
34 | static int source; | 35 | static struct pps_device *pps; |
35 | static struct timer_list ktimer; | 36 | static struct timer_list ktimer; |
36 | 37 | ||
37 | /* | 38 | /* |
@@ -40,19 +41,12 @@ static struct timer_list ktimer; | |||
40 | 41 | ||
41 | static void pps_ktimer_event(unsigned long ptr) | 42 | static 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 | ||
64 | static void pps_ktimer_echo(int source, int event, void *data) | 58 | static 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 | ||
90 | static void __exit pps_ktimer_exit(void) | 83 | static 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 | ||
98 | static int __init pps_ktimer_init(void) | 91 | static 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 | ||
118 | module_init(pps_ktimer_init); | 108 | module_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 | ||
29 | static void pps_tty_dcd_change(struct tty_struct *tty, unsigned int status, | 31 | static 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 | ||
56 | static int (*alias_n_tty_open)(struct tty_struct *tty); | 46 | static 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 | |||
83 | err_unregister: | ||
84 | tty->disc_data = NULL; | ||
85 | pps_unregister_source(pps); | ||
86 | return ret; | ||
89 | } | 87 | } |
90 | 88 | ||
91 | static void (*alias_n_tty_close)(struct tty_struct *tty); | 89 | static void (*alias_n_tty_close)(struct tty_struct *tty); |
92 | 90 | ||
93 | static void pps_tty_close(struct tty_struct *tty) | 91 | static 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 | ||
103 | static struct tty_ldisc_ops pps_ldisc_ops; | 102 | static 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 | |||
45 | static unsigned int clear_wait = 100; | ||
46 | MODULE_PARM_DESC(clear_wait, | ||
47 | "Maximum number of port reads when polling for signal clear," | ||
48 | " zero turns clear edge capture off entirely"); | ||
49 | module_param(clear_wait, uint, 0); | ||
50 | |||
51 | |||
52 | /* internal per port structure */ | ||
53 | struct 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 | |||
60 | static 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 */ | ||
66 | static 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 | |||
120 | out_assert: | ||
121 | /* fire assert event */ | ||
122 | pps_event(dev->pps, &ts_assert, | ||
123 | PPS_CAPTUREASSERT, NULL); | ||
124 | return; | ||
125 | |||
126 | out_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 */ | ||
137 | static 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 | |||
144 | static 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 | |||
192 | err_release_dev: | ||
193 | parport_release(device->pardev); | ||
194 | err_unregister_dev: | ||
195 | parport_unregister_device(device->pardev); | ||
196 | err_free: | ||
197 | kfree(device); | ||
198 | } | ||
199 | |||
200 | static 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 | |||
219 | static struct parport_driver pps_parport_driver = { | ||
220 | .name = KBUILD_MODNAME, | ||
221 | .attach = parport_attach, | ||
222 | .detach = parport_detach, | ||
223 | }; | ||
224 | |||
225 | /* module staff */ | ||
226 | |||
227 | static 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 | |||
248 | static void __exit pps_parport_exit(void) | ||
249 | { | ||
250 | parport_unregister_driver(&pps_parport_driver); | ||
251 | } | ||
252 | |||
253 | module_init(pps_parport_init); | ||
254 | module_exit(pps_parport_exit); | ||
255 | |||
256 | MODULE_AUTHOR("Alexander Gordeev <lasaine@lvk.cs.msu.su>"); | ||
257 | MODULE_DESCRIPTION(DRVDESC); | ||
258 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/pps/generators/Kconfig b/drivers/pps/generators/Kconfig new file mode 100644 index 000000000000..e4c4f3dc0728 --- /dev/null +++ b/drivers/pps/generators/Kconfig | |||
@@ -0,0 +1,13 @@ | |||
1 | # | ||
2 | # PPS generators configuration | ||
3 | # | ||
4 | |||
5 | comment "PPS generators support" | ||
6 | |||
7 | config PPS_GENERATOR_PARPORT | ||
8 | tristate "Parallel port PPS signal generator" | ||
9 | depends on PARPORT && BROKEN | ||
10 | help | ||
11 | If you say yes here you get support for a PPS signal generator which | ||
12 | utilizes STROBE pin of a parallel port to send PPS signals. It uses | ||
13 | parport abstraction layer and hrtimers to precisely control the signal. | ||
diff --git a/drivers/pps/generators/Makefile b/drivers/pps/generators/Makefile new file mode 100644 index 000000000000..303304a6b8ec --- /dev/null +++ b/drivers/pps/generators/Makefile | |||
@@ -0,0 +1,9 @@ | |||
1 | # | ||
2 | # Makefile for PPS generators. | ||
3 | # | ||
4 | |||
5 | obj-$(CONFIG_PPS_GENERATOR_PARPORT) += pps_gen_parport.o | ||
6 | |||
7 | ifeq ($(CONFIG_PPS_DEBUG),y) | ||
8 | EXTRA_CFLAGS += -DDEBUG | ||
9 | endif | ||
diff --git a/drivers/pps/generators/pps_gen_parport.c b/drivers/pps/generators/pps_gen_parport.c new file mode 100644 index 000000000000..dcd39fba6ddd --- /dev/null +++ b/drivers/pps/generators/pps_gen_parport.c | |||
@@ -0,0 +1,277 @@ | |||
1 | /* | ||
2 | * pps_gen_parport.c -- kernel parallel port PPS signal generator | ||
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 | * fix issues when realtime clock is adjusted in a leap | ||
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/time.h> | ||
34 | #include <linux/hrtimer.h> | ||
35 | #include <linux/parport.h> | ||
36 | |||
37 | #define DRVDESC "parallel port PPS signal generator" | ||
38 | |||
39 | #define SIGNAL 0 | ||
40 | #define NO_SIGNAL PARPORT_CONTROL_STROBE | ||
41 | |||
42 | /* module parameters */ | ||
43 | |||
44 | #define SEND_DELAY_MAX 100000 | ||
45 | |||
46 | static unsigned int send_delay = 30000; | ||
47 | MODULE_PARM_DESC(delay, | ||
48 | "Delay between setting and dropping the signal (ns)"); | ||
49 | module_param_named(delay, send_delay, uint, 0); | ||
50 | |||
51 | |||
52 | #define SAFETY_INTERVAL 3000 /* set the hrtimer earlier for safety (ns) */ | ||
53 | |||
54 | /* internal per port structure */ | ||
55 | struct pps_generator_pp { | ||
56 | struct pardevice *pardev; /* parport device */ | ||
57 | struct hrtimer timer; | ||
58 | long port_write_time; /* calibrated port write time (ns) */ | ||
59 | }; | ||
60 | |||
61 | static struct pps_generator_pp device = { | ||
62 | .pardev = NULL, | ||
63 | }; | ||
64 | |||
65 | static int attached; | ||
66 | |||
67 | /* calibrated time between a hrtimer event and the reaction */ | ||
68 | static long hrtimer_error = SAFETY_INTERVAL; | ||
69 | |||
70 | /* the kernel hrtimer event */ | ||
71 | static enum hrtimer_restart hrtimer_event(struct hrtimer *timer) | ||
72 | { | ||
73 | struct timespec expire_time, ts1, ts2, ts3, dts; | ||
74 | struct pps_generator_pp *dev; | ||
75 | struct parport *port; | ||
76 | long lim, delta; | ||
77 | unsigned long flags; | ||
78 | |||
79 | /* We have to disable interrupts here. The idea is to prevent | ||
80 | * other interrupts on the same processor to introduce random | ||
81 | * lags while polling the clock. getnstimeofday() takes <1us on | ||
82 | * most machines while other interrupt handlers can take much | ||
83 | * more potentially. | ||
84 | * | ||
85 | * NB: approx time with blocked interrupts = | ||
86 | * send_delay + 3 * SAFETY_INTERVAL | ||
87 | */ | ||
88 | local_irq_save(flags); | ||
89 | |||
90 | /* first of all we get the time stamp... */ | ||
91 | getnstimeofday(&ts1); | ||
92 | expire_time = ktime_to_timespec(hrtimer_get_softexpires(timer)); | ||
93 | dev = container_of(timer, struct pps_generator_pp, timer); | ||
94 | lim = NSEC_PER_SEC - send_delay - dev->port_write_time; | ||
95 | |||
96 | /* check if we are late */ | ||
97 | if (expire_time.tv_sec != ts1.tv_sec || ts1.tv_nsec > lim) { | ||
98 | local_irq_restore(flags); | ||
99 | pr_err("we are late this time %ld.%09ld\n", | ||
100 | ts1.tv_sec, ts1.tv_nsec); | ||
101 | goto done; | ||
102 | } | ||
103 | |||
104 | /* busy loop until the time is right for an assert edge */ | ||
105 | do { | ||
106 | getnstimeofday(&ts2); | ||
107 | } while (expire_time.tv_sec == ts2.tv_sec && ts2.tv_nsec < lim); | ||
108 | |||
109 | /* set the signal */ | ||
110 | port = dev->pardev->port; | ||
111 | port->ops->write_control(port, SIGNAL); | ||
112 | |||
113 | /* busy loop until the time is right for a clear edge */ | ||
114 | lim = NSEC_PER_SEC - dev->port_write_time; | ||
115 | do { | ||
116 | getnstimeofday(&ts2); | ||
117 | } while (expire_time.tv_sec == ts2.tv_sec && ts2.tv_nsec < lim); | ||
118 | |||
119 | /* unset the signal */ | ||
120 | port->ops->write_control(port, NO_SIGNAL); | ||
121 | |||
122 | getnstimeofday(&ts3); | ||
123 | |||
124 | local_irq_restore(flags); | ||
125 | |||
126 | /* update calibrated port write time */ | ||
127 | dts = timespec_sub(ts3, ts2); | ||
128 | dev->port_write_time = | ||
129 | (dev->port_write_time + timespec_to_ns(&dts)) >> 1; | ||
130 | |||
131 | done: | ||
132 | /* update calibrated hrtimer error */ | ||
133 | dts = timespec_sub(ts1, expire_time); | ||
134 | delta = timespec_to_ns(&dts); | ||
135 | /* If the new error value is bigger then the old, use the new | ||
136 | * value, if not then slowly move towards the new value. This | ||
137 | * way it should be safe in bad conditions and efficient in | ||
138 | * good conditions. | ||
139 | */ | ||
140 | if (delta >= hrtimer_error) | ||
141 | hrtimer_error = delta; | ||
142 | else | ||
143 | hrtimer_error = (3 * hrtimer_error + delta) >> 2; | ||
144 | |||
145 | /* update the hrtimer expire time */ | ||
146 | hrtimer_set_expires(timer, | ||
147 | ktime_set(expire_time.tv_sec + 1, | ||
148 | NSEC_PER_SEC - (send_delay + | ||
149 | dev->port_write_time + SAFETY_INTERVAL + | ||
150 | 2 * hrtimer_error))); | ||
151 | |||
152 | return HRTIMER_RESTART; | ||
153 | } | ||
154 | |||
155 | /* calibrate port write time */ | ||
156 | #define PORT_NTESTS_SHIFT 5 | ||
157 | static void calibrate_port(struct pps_generator_pp *dev) | ||
158 | { | ||
159 | struct parport *port = dev->pardev->port; | ||
160 | int i; | ||
161 | long acc = 0; | ||
162 | |||
163 | for (i = 0; i < (1 << PORT_NTESTS_SHIFT); i++) { | ||
164 | struct timespec a, b; | ||
165 | unsigned long irq_flags; | ||
166 | |||
167 | local_irq_save(irq_flags); | ||
168 | getnstimeofday(&a); | ||
169 | port->ops->write_control(port, NO_SIGNAL); | ||
170 | getnstimeofday(&b); | ||
171 | local_irq_restore(irq_flags); | ||
172 | |||
173 | b = timespec_sub(b, a); | ||
174 | acc += timespec_to_ns(&b); | ||
175 | } | ||
176 | |||
177 | dev->port_write_time = acc >> PORT_NTESTS_SHIFT; | ||
178 | pr_info("port write takes %ldns\n", dev->port_write_time); | ||
179 | } | ||
180 | |||
181 | static inline ktime_t next_intr_time(struct pps_generator_pp *dev) | ||
182 | { | ||
183 | struct timespec ts; | ||
184 | |||
185 | getnstimeofday(&ts); | ||
186 | |||
187 | return ktime_set(ts.tv_sec + | ||
188 | ((ts.tv_nsec > 990 * NSEC_PER_MSEC) ? 1 : 0), | ||
189 | NSEC_PER_SEC - (send_delay + | ||
190 | dev->port_write_time + 3 * SAFETY_INTERVAL)); | ||
191 | } | ||
192 | |||
193 | static void parport_attach(struct parport *port) | ||
194 | { | ||
195 | if (attached) { | ||
196 | /* we already have a port */ | ||
197 | return; | ||
198 | } | ||
199 | |||
200 | device.pardev = parport_register_device(port, KBUILD_MODNAME, | ||
201 | NULL, NULL, NULL, PARPORT_FLAG_EXCL, &device); | ||
202 | if (!device.pardev) { | ||
203 | pr_err("couldn't register with %s\n", port->name); | ||
204 | return; | ||
205 | } | ||
206 | |||
207 | if (parport_claim_or_block(device.pardev) < 0) { | ||
208 | pr_err("couldn't claim %s\n", port->name); | ||
209 | goto err_unregister_dev; | ||
210 | } | ||
211 | |||
212 | pr_info("attached to %s\n", port->name); | ||
213 | attached = 1; | ||
214 | |||
215 | calibrate_port(&device); | ||
216 | |||
217 | hrtimer_init(&device.timer, CLOCK_REALTIME, HRTIMER_MODE_ABS); | ||
218 | device.timer.function = hrtimer_event; | ||
219 | hrtimer_start(&device.timer, next_intr_time(&device), HRTIMER_MODE_ABS); | ||
220 | |||
221 | return; | ||
222 | |||
223 | err_unregister_dev: | ||
224 | parport_unregister_device(device.pardev); | ||
225 | } | ||
226 | |||
227 | static void parport_detach(struct parport *port) | ||
228 | { | ||
229 | if (port->cad != device.pardev) | ||
230 | return; /* not our port */ | ||
231 | |||
232 | hrtimer_cancel(&device.timer); | ||
233 | parport_release(device.pardev); | ||
234 | parport_unregister_device(device.pardev); | ||
235 | } | ||
236 | |||
237 | static struct parport_driver pps_gen_parport_driver = { | ||
238 | .name = KBUILD_MODNAME, | ||
239 | .attach = parport_attach, | ||
240 | .detach = parport_detach, | ||
241 | }; | ||
242 | |||
243 | /* module staff */ | ||
244 | |||
245 | static int __init pps_gen_parport_init(void) | ||
246 | { | ||
247 | int ret; | ||
248 | |||
249 | pr_info(DRVDESC "\n"); | ||
250 | |||
251 | if (send_delay > SEND_DELAY_MAX) { | ||
252 | pr_err("delay value should be not greater" | ||
253 | " then %d\n", SEND_DELAY_MAX); | ||
254 | return -EINVAL; | ||
255 | } | ||
256 | |||
257 | ret = parport_register_driver(&pps_gen_parport_driver); | ||
258 | if (ret) { | ||
259 | pr_err("unable to register with parport\n"); | ||
260 | return ret; | ||
261 | } | ||
262 | |||
263 | return 0; | ||
264 | } | ||
265 | |||
266 | static void __exit pps_gen_parport_exit(void) | ||
267 | { | ||
268 | parport_unregister_driver(&pps_gen_parport_driver); | ||
269 | pr_info("hrtimer avg error is %ldns\n", hrtimer_error); | ||
270 | } | ||
271 | |||
272 | module_init(pps_gen_parport_init); | ||
273 | module_exit(pps_gen_parport_exit); | ||
274 | |||
275 | MODULE_AUTHOR("Alexander Gordeev <lasaine@lvk.cs.msu.su>"); | ||
276 | MODULE_DESCRIPTION(DRVDESC); | ||
277 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/pps/kapi.c b/drivers/pps/kapi.c index 1aa02db3ff4e..a4e8eb9fece6 100644 --- a/drivers/pps/kapi.c +++ b/drivers/pps/kapi.c | |||
@@ -19,24 +19,20 @@ | |||
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> |
25 | #include <linux/init.h> | 26 | #include <linux/init.h> |
26 | #include <linux/sched.h> | 27 | #include <linux/sched.h> |
27 | #include <linux/time.h> | 28 | #include <linux/time.h> |
29 | #include <linux/timex.h> | ||
28 | #include <linux/spinlock.h> | 30 | #include <linux/spinlock.h> |
29 | #include <linux/idr.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 | ||
34 | /* | 35 | #include "kc.h" |
35 | * Global variables | ||
36 | */ | ||
37 | |||
38 | DEFINE_SPINLOCK(pps_idr_lock); | ||
39 | DEFINE_IDR(pps_idr); | ||
40 | 36 | ||
41 | /* | 37 | /* |
42 | * Local functions | 38 | * Local functions |
@@ -60,60 +56,6 @@ static void pps_add_offset(struct pps_ktime *ts, struct pps_ktime *offset) | |||
60 | * Exported functions | 56 | * Exported functions |
61 | */ | 57 | */ |
62 | 58 | ||
63 | /* pps_get_source - find a PPS source | ||
64 | * @source: the PPS source ID. | ||
65 | * | ||
66 | * This function is used to find an already registered PPS source into the | ||
67 | * system. | ||
68 | * | ||
69 | * The function returns NULL if found nothing, otherwise it returns a pointer | ||
70 | * to the PPS source data struct (the refcounter is incremented by 1). | ||
71 | */ | ||
72 | |||
73 | struct pps_device *pps_get_source(int source) | ||
74 | { | ||
75 | struct pps_device *pps; | ||
76 | unsigned long flags; | ||
77 | |||
78 | spin_lock_irqsave(&pps_idr_lock, flags); | ||
79 | |||
80 | pps = idr_find(&pps_idr, source); | ||
81 | if (pps != NULL) | ||
82 | atomic_inc(&pps->usage); | ||
83 | |||
84 | spin_unlock_irqrestore(&pps_idr_lock, flags); | ||
85 | |||
86 | return pps; | ||
87 | } | ||
88 | |||
89 | /* pps_put_source - free the PPS source data | ||
90 | * @pps: a pointer to the PPS source. | ||
91 | * | ||
92 | * This function is used to free a PPS data struct if its refcount is 0. | ||
93 | */ | ||
94 | |||
95 | void pps_put_source(struct pps_device *pps) | ||
96 | { | ||
97 | unsigned long flags; | ||
98 | |||
99 | spin_lock_irqsave(&pps_idr_lock, flags); | ||
100 | BUG_ON(atomic_read(&pps->usage) == 0); | ||
101 | |||
102 | if (!atomic_dec_and_test(&pps->usage)) { | ||
103 | pps = NULL; | ||
104 | goto exit; | ||
105 | } | ||
106 | |||
107 | /* No more reference to the PPS source. We can safely remove the | ||
108 | * PPS data struct. | ||
109 | */ | ||
110 | idr_remove(&pps_idr, pps->id); | ||
111 | |||
112 | exit: | ||
113 | spin_unlock_irqrestore(&pps_idr_lock, flags); | ||
114 | kfree(pps); | ||
115 | } | ||
116 | |||
117 | /* pps_register_source - add a PPS source in the system | 59 | /* pps_register_source - add a PPS source in the system |
118 | * @info: the PPS info struct | 60 | * @info: the PPS info struct |
119 | * @default_params: the default PPS parameters of the new source | 61 | * @default_params: the default PPS parameters of the new source |
@@ -122,31 +64,31 @@ exit: | |||
122 | * source is described by info's fields and it will have, as default PPS | 64 | * source is described by info's fields and it will have, as default PPS |
123 | * parameters, the ones specified into default_params. | 65 | * parameters, the ones specified into default_params. |
124 | * | 66 | * |
125 | * The function returns, in case of success, the PPS source ID. | 67 | * The function returns, in case of success, the PPS device. Otherwise NULL. |
126 | */ | 68 | */ |
127 | 69 | ||
128 | int pps_register_source(struct pps_source_info *info, int default_params) | 70 | struct pps_device *pps_register_source(struct pps_source_info *info, |
71 | int default_params) | ||
129 | { | 72 | { |
130 | struct pps_device *pps; | 73 | struct pps_device *pps; |
131 | int id; | ||
132 | int err; | 74 | int err; |
133 | 75 | ||
134 | /* Sanity checks */ | 76 | /* Sanity checks */ |
135 | if ((info->mode & default_params) != default_params) { | 77 | if ((info->mode & default_params) != default_params) { |
136 | printk(KERN_ERR "pps: %s: unsupported default parameters\n", | 78 | pr_err("%s: unsupported default parameters\n", |
137 | info->name); | 79 | info->name); |
138 | err = -EINVAL; | 80 | err = -EINVAL; |
139 | goto pps_register_source_exit; | 81 | goto pps_register_source_exit; |
140 | } | 82 | } |
141 | if ((info->mode & (PPS_ECHOASSERT | PPS_ECHOCLEAR)) != 0 && | 83 | if ((info->mode & (PPS_ECHOASSERT | PPS_ECHOCLEAR)) != 0 && |
142 | info->echo == NULL) { | 84 | info->echo == NULL) { |
143 | printk(KERN_ERR "pps: %s: echo function is not defined\n", | 85 | pr_err("%s: echo function is not defined\n", |
144 | info->name); | 86 | info->name); |
145 | err = -EINVAL; | 87 | err = -EINVAL; |
146 | goto pps_register_source_exit; | 88 | goto pps_register_source_exit; |
147 | } | 89 | } |
148 | if ((info->mode & (PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP)) == 0) { | 90 | if ((info->mode & (PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP)) == 0) { |
149 | printk(KERN_ERR "pps: %s: unspecified time format\n", | 91 | pr_err("%s: unspecified time format\n", |
150 | info->name); | 92 | info->name); |
151 | err = -EINVAL; | 93 | err = -EINVAL; |
152 | goto pps_register_source_exit; | 94 | goto pps_register_source_exit; |
@@ -168,94 +110,48 @@ int pps_register_source(struct pps_source_info *info, int default_params) | |||
168 | 110 | ||
169 | init_waitqueue_head(&pps->queue); | 111 | init_waitqueue_head(&pps->queue); |
170 | spin_lock_init(&pps->lock); | 112 | spin_lock_init(&pps->lock); |
171 | atomic_set(&pps->usage, 1); | ||
172 | |||
173 | /* Get new ID for the new PPS source */ | ||
174 | if (idr_pre_get(&pps_idr, GFP_KERNEL) == 0) { | ||
175 | err = -ENOMEM; | ||
176 | goto kfree_pps; | ||
177 | } | ||
178 | |||
179 | spin_lock_irq(&pps_idr_lock); | ||
180 | |||
181 | /* Now really allocate the PPS source. | ||
182 | * After idr_get_new() calling the new source will be freely available | ||
183 | * into the kernel. | ||
184 | */ | ||
185 | err = idr_get_new(&pps_idr, pps, &id); | ||
186 | if (err < 0) { | ||
187 | spin_unlock_irq(&pps_idr_lock); | ||
188 | goto kfree_pps; | ||
189 | } | ||
190 | |||
191 | id = id & MAX_ID_MASK; | ||
192 | if (id >= PPS_MAX_SOURCES) { | ||
193 | spin_unlock_irq(&pps_idr_lock); | ||
194 | |||
195 | printk(KERN_ERR "pps: %s: too many PPS sources in the system\n", | ||
196 | info->name); | ||
197 | err = -EBUSY; | ||
198 | goto free_idr; | ||
199 | } | ||
200 | pps->id = id; | ||
201 | |||
202 | spin_unlock_irq(&pps_idr_lock); | ||
203 | 113 | ||
204 | /* Create the char device */ | 114 | /* Create the char device */ |
205 | err = pps_register_cdev(pps); | 115 | err = pps_register_cdev(pps); |
206 | if (err < 0) { | 116 | if (err < 0) { |
207 | printk(KERN_ERR "pps: %s: unable to create char device\n", | 117 | pr_err("%s: unable to create char device\n", |
208 | info->name); | 118 | info->name); |
209 | goto free_idr; | 119 | goto kfree_pps; |
210 | } | 120 | } |
211 | 121 | ||
212 | pr_info("new PPS source %s at ID %d\n", info->name, id); | 122 | dev_info(pps->dev, "new PPS source %s\n", info->name); |
213 | 123 | ||
214 | return id; | 124 | return pps; |
215 | |||
216 | free_idr: | ||
217 | spin_lock_irq(&pps_idr_lock); | ||
218 | idr_remove(&pps_idr, id); | ||
219 | spin_unlock_irq(&pps_idr_lock); | ||
220 | 125 | ||
221 | kfree_pps: | 126 | kfree_pps: |
222 | kfree(pps); | 127 | kfree(pps); |
223 | 128 | ||
224 | pps_register_source_exit: | 129 | pps_register_source_exit: |
225 | printk(KERN_ERR "pps: %s: unable to register source\n", info->name); | 130 | pr_err("%s: unable to register source\n", info->name); |
226 | 131 | ||
227 | return err; | 132 | return NULL; |
228 | } | 133 | } |
229 | EXPORT_SYMBOL(pps_register_source); | 134 | EXPORT_SYMBOL(pps_register_source); |
230 | 135 | ||
231 | /* pps_unregister_source - remove a PPS source from the system | 136 | /* pps_unregister_source - remove a PPS source from the system |
232 | * @source: the PPS source ID | 137 | * @pps: the PPS source |
233 | * | 138 | * |
234 | * This function is used to remove a previously registered PPS source from | 139 | * This function is used to remove a previously registered PPS source from |
235 | * the system. | 140 | * the system. |
236 | */ | 141 | */ |
237 | 142 | ||
238 | void pps_unregister_source(int source) | 143 | void pps_unregister_source(struct pps_device *pps) |
239 | { | 144 | { |
240 | struct pps_device *pps; | 145 | pps_kc_remove(pps); |
241 | |||
242 | spin_lock_irq(&pps_idr_lock); | ||
243 | pps = idr_find(&pps_idr, source); | ||
244 | |||
245 | if (!pps) { | ||
246 | BUG(); | ||
247 | spin_unlock_irq(&pps_idr_lock); | ||
248 | return; | ||
249 | } | ||
250 | spin_unlock_irq(&pps_idr_lock); | ||
251 | |||
252 | pps_unregister_cdev(pps); | 146 | pps_unregister_cdev(pps); |
253 | pps_put_source(pps); | 147 | |
148 | /* don't have to kfree(pps) here because it will be done on | ||
149 | * device destruction */ | ||
254 | } | 150 | } |
255 | EXPORT_SYMBOL(pps_unregister_source); | 151 | EXPORT_SYMBOL(pps_unregister_source); |
256 | 152 | ||
257 | /* pps_event - register a PPS event into the system | 153 | /* pps_event - register a PPS event into the system |
258 | * @source: the PPS source ID | 154 | * @pps: the PPS device |
259 | * @ts: the event timestamp | 155 | * @ts: the event timestamp |
260 | * @event: the event type | 156 | * @event: the event type |
261 | * @data: userdef pointer | 157 | * @data: userdef pointer |
@@ -263,78 +159,72 @@ EXPORT_SYMBOL(pps_unregister_source); | |||
263 | * This function is used by each PPS client in order to register a new | 159 | * This function is used by each PPS client in order to register a new |
264 | * PPS event into the system (it's usually called inside an IRQ handler). | 160 | * PPS event into the system (it's usually called inside an IRQ handler). |
265 | * | 161 | * |
266 | * If an echo function is associated with the PPS source it will be called | 162 | * If an echo function is associated with the PPS device it will be called |
267 | * as: | 163 | * as: |
268 | * pps->info.echo(source, event, data); | 164 | * pps->info.echo(pps, event, data); |
269 | */ | 165 | */ |
270 | 166 | void pps_event(struct pps_device *pps, struct pps_event_time *ts, int event, | |
271 | void pps_event(int source, struct pps_ktime *ts, int event, void *data) | 167 | void *data) |
272 | { | 168 | { |
273 | struct pps_device *pps; | ||
274 | unsigned long flags; | 169 | unsigned long flags; |
275 | int captured = 0; | 170 | int captured = 0; |
171 | struct pps_ktime ts_real = { .sec = 0, .nsec = 0, .flags = 0 }; | ||
276 | 172 | ||
277 | if ((event & (PPS_CAPTUREASSERT | PPS_CAPTURECLEAR)) == 0) { | 173 | /* check event type */ |
278 | printk(KERN_ERR "pps: unknown event (%x) for source %d\n", | 174 | BUG_ON((event & (PPS_CAPTUREASSERT | PPS_CAPTURECLEAR)) == 0); |
279 | event, source); | ||
280 | return; | ||
281 | } | ||
282 | 175 | ||
283 | pps = pps_get_source(source); | 176 | dev_dbg(pps->dev, "PPS event at %ld.%09ld\n", |
284 | if (!pps) | 177 | ts->ts_real.tv_sec, ts->ts_real.tv_nsec); |
285 | return; | ||
286 | 178 | ||
287 | pr_debug("PPS event on source %d at %llu.%06u\n", | 179 | timespec_to_pps_ktime(&ts_real, ts->ts_real); |
288 | pps->id, (unsigned long long) ts->sec, ts->nsec); | ||
289 | 180 | ||
290 | spin_lock_irqsave(&pps->lock, flags); | 181 | spin_lock_irqsave(&pps->lock, flags); |
291 | 182 | ||
292 | /* Must call the echo function? */ | 183 | /* Must call the echo function? */ |
293 | if ((pps->params.mode & (PPS_ECHOASSERT | PPS_ECHOCLEAR))) | 184 | if ((pps->params.mode & (PPS_ECHOASSERT | PPS_ECHOCLEAR))) |
294 | pps->info.echo(source, event, data); | 185 | pps->info.echo(pps, event, data); |
295 | 186 | ||
296 | /* Check the event */ | 187 | /* Check the event */ |
297 | pps->current_mode = pps->params.mode; | 188 | pps->current_mode = pps->params.mode; |
298 | if ((event & PPS_CAPTUREASSERT) & | 189 | if (event & pps->params.mode & PPS_CAPTUREASSERT) { |
299 | (pps->params.mode & PPS_CAPTUREASSERT)) { | ||
300 | /* We have to add an offset? */ | 190 | /* We have to add an offset? */ |
301 | if (pps->params.mode & PPS_OFFSETASSERT) | 191 | if (pps->params.mode & PPS_OFFSETASSERT) |
302 | pps_add_offset(ts, &pps->params.assert_off_tu); | 192 | pps_add_offset(&ts_real, |
193 | &pps->params.assert_off_tu); | ||
303 | 194 | ||
304 | /* Save the time stamp */ | 195 | /* Save the time stamp */ |
305 | pps->assert_tu = *ts; | 196 | pps->assert_tu = ts_real; |
306 | pps->assert_sequence++; | 197 | pps->assert_sequence++; |
307 | pr_debug("capture assert seq #%u for source %d\n", | 198 | dev_dbg(pps->dev, "capture assert seq #%u\n", |
308 | pps->assert_sequence, source); | 199 | pps->assert_sequence); |
309 | 200 | ||
310 | captured = ~0; | 201 | captured = ~0; |
311 | } | 202 | } |
312 | if ((event & PPS_CAPTURECLEAR) & | 203 | if (event & pps->params.mode & PPS_CAPTURECLEAR) { |
313 | (pps->params.mode & PPS_CAPTURECLEAR)) { | ||
314 | /* We have to add an offset? */ | 204 | /* We have to add an offset? */ |
315 | if (pps->params.mode & PPS_OFFSETCLEAR) | 205 | if (pps->params.mode & PPS_OFFSETCLEAR) |
316 | pps_add_offset(ts, &pps->params.clear_off_tu); | 206 | pps_add_offset(&ts_real, |
207 | &pps->params.clear_off_tu); | ||
317 | 208 | ||
318 | /* Save the time stamp */ | 209 | /* Save the time stamp */ |
319 | pps->clear_tu = *ts; | 210 | pps->clear_tu = ts_real; |
320 | pps->clear_sequence++; | 211 | pps->clear_sequence++; |
321 | pr_debug("capture clear seq #%u for source %d\n", | 212 | dev_dbg(pps->dev, "capture clear seq #%u\n", |
322 | pps->clear_sequence, source); | 213 | pps->clear_sequence); |
323 | 214 | ||
324 | captured = ~0; | 215 | captured = ~0; |
325 | } | 216 | } |
326 | 217 | ||
327 | /* Wake up iif captured somthing */ | 218 | pps_kc_event(pps, ts, event); |
219 | |||
220 | /* Wake up if captured something */ | ||
328 | if (captured) { | 221 | if (captured) { |
329 | pps->go = ~0; | 222 | pps->last_ev++; |
330 | wake_up_interruptible(&pps->queue); | 223 | wake_up_interruptible_all(&pps->queue); |
331 | 224 | ||
332 | kill_fasync(&pps->async_queue, SIGIO, POLL_IN); | 225 | kill_fasync(&pps->async_queue, SIGIO, POLL_IN); |
333 | } | 226 | } |
334 | 227 | ||
335 | spin_unlock_irqrestore(&pps->lock, flags); | 228 | spin_unlock_irqrestore(&pps->lock, flags); |
336 | |||
337 | /* Now we can release the PPS source for (possible) deregistration */ | ||
338 | pps_put_source(pps); | ||
339 | } | 229 | } |
340 | EXPORT_SYMBOL(pps_event); | 230 | EXPORT_SYMBOL(pps_event); |
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 ca5183bdad85..2baadd21b7a6 100644 --- a/drivers/pps/pps.c +++ b/drivers/pps/pps.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> |
@@ -26,9 +27,13 @@ | |||
26 | #include <linux/sched.h> | 27 | #include <linux/sched.h> |
27 | #include <linux/uaccess.h> | 28 | #include <linux/uaccess.h> |
28 | #include <linux/idr.h> | 29 | #include <linux/idr.h> |
30 | #include <linux/mutex.h> | ||
29 | #include <linux/cdev.h> | 31 | #include <linux/cdev.h> |
30 | #include <linux/poll.h> | 32 | #include <linux/poll.h> |
31 | #include <linux/pps_kernel.h> | 33 | #include <linux/pps_kernel.h> |
34 | #include <linux/slab.h> | ||
35 | |||
36 | #include "kc.h" | ||
32 | 37 | ||
33 | /* | 38 | /* |
34 | * Local variables | 39 | * Local variables |
@@ -37,6 +42,9 @@ | |||
37 | static dev_t pps_devt; | 42 | static dev_t pps_devt; |
38 | static struct class *pps_class; | 43 | static struct class *pps_class; |
39 | 44 | ||
45 | static DEFINE_MUTEX(pps_idr_lock); | ||
46 | static DEFINE_IDR(pps_idr); | ||
47 | |||
40 | /* | 48 | /* |
41 | * Char device methods | 49 | * Char device methods |
42 | */ | 50 | */ |
@@ -61,15 +69,13 @@ static long pps_cdev_ioctl(struct file *file, | |||
61 | { | 69 | { |
62 | struct pps_device *pps = file->private_data; | 70 | struct pps_device *pps = file->private_data; |
63 | struct pps_kparams params; | 71 | struct pps_kparams params; |
64 | struct pps_fdata fdata; | ||
65 | unsigned long ticks; | ||
66 | void __user *uarg = (void __user *) arg; | 72 | void __user *uarg = (void __user *) arg; |
67 | int __user *iuarg = (int __user *) arg; | 73 | int __user *iuarg = (int __user *) arg; |
68 | int err; | 74 | int err; |
69 | 75 | ||
70 | switch (cmd) { | 76 | switch (cmd) { |
71 | case PPS_GETPARAMS: | 77 | case PPS_GETPARAMS: |
72 | pr_debug("PPS_GETPARAMS: source %d\n", pps->id); | 78 | dev_dbg(pps->dev, "PPS_GETPARAMS\n"); |
73 | 79 | ||
74 | spin_lock_irq(&pps->lock); | 80 | spin_lock_irq(&pps->lock); |
75 | 81 | ||
@@ -85,7 +91,7 @@ static long pps_cdev_ioctl(struct file *file, | |||
85 | break; | 91 | break; |
86 | 92 | ||
87 | case PPS_SETPARAMS: | 93 | case PPS_SETPARAMS: |
88 | pr_debug("PPS_SETPARAMS: source %d\n", pps->id); | 94 | dev_dbg(pps->dev, "PPS_SETPARAMS\n"); |
89 | 95 | ||
90 | /* Check the capabilities */ | 96 | /* Check the capabilities */ |
91 | if (!capable(CAP_SYS_TIME)) | 97 | if (!capable(CAP_SYS_TIME)) |
@@ -95,14 +101,14 @@ static long pps_cdev_ioctl(struct file *file, | |||
95 | if (err) | 101 | if (err) |
96 | return -EFAULT; | 102 | return -EFAULT; |
97 | if (!(params.mode & (PPS_CAPTUREASSERT | PPS_CAPTURECLEAR))) { | 103 | if (!(params.mode & (PPS_CAPTUREASSERT | PPS_CAPTURECLEAR))) { |
98 | pr_debug("capture mode unspecified (%x)\n", | 104 | dev_dbg(pps->dev, "capture mode unspecified (%x)\n", |
99 | params.mode); | 105 | params.mode); |
100 | return -EINVAL; | 106 | return -EINVAL; |
101 | } | 107 | } |
102 | 108 | ||
103 | /* Check for supported capabilities */ | 109 | /* Check for supported capabilities */ |
104 | if ((params.mode & ~pps->info.mode) != 0) { | 110 | if ((params.mode & ~pps->info.mode) != 0) { |
105 | pr_debug("unsupported capabilities (%x)\n", | 111 | dev_dbg(pps->dev, "unsupported capabilities (%x)\n", |
106 | params.mode); | 112 | params.mode); |
107 | return -EINVAL; | 113 | return -EINVAL; |
108 | } | 114 | } |
@@ -115,7 +121,7 @@ static long pps_cdev_ioctl(struct file *file, | |||
115 | /* Restore the read only parameters */ | 121 | /* Restore the read only parameters */ |
116 | if ((params.mode & (PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP)) == 0) { | 122 | if ((params.mode & (PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP)) == 0) { |
117 | /* section 3.3 of RFC 2783 interpreted */ | 123 | /* section 3.3 of RFC 2783 interpreted */ |
118 | pr_debug("time format unspecified (%x)\n", | 124 | dev_dbg(pps->dev, "time format unspecified (%x)\n", |
119 | params.mode); | 125 | params.mode); |
120 | pps->params.mode |= PPS_TSFMT_TSPEC; | 126 | pps->params.mode |= PPS_TSFMT_TSPEC; |
121 | } | 127 | } |
@@ -128,7 +134,7 @@ static long pps_cdev_ioctl(struct file *file, | |||
128 | break; | 134 | break; |
129 | 135 | ||
130 | case PPS_GETCAP: | 136 | case PPS_GETCAP: |
131 | pr_debug("PPS_GETCAP: source %d\n", pps->id); | 137 | dev_dbg(pps->dev, "PPS_GETCAP\n"); |
132 | 138 | ||
133 | err = put_user(pps->info.mode, iuarg); | 139 | err = put_user(pps->info.mode, iuarg); |
134 | if (err) | 140 | if (err) |
@@ -136,20 +142,26 @@ static long pps_cdev_ioctl(struct file *file, | |||
136 | 142 | ||
137 | break; | 143 | break; |
138 | 144 | ||
139 | case PPS_FETCH: | 145 | case PPS_FETCH: { |
140 | pr_debug("PPS_FETCH: source %d\n", pps->id); | 146 | struct pps_fdata fdata; |
147 | unsigned int ev; | ||
148 | |||
149 | dev_dbg(pps->dev, "PPS_FETCH\n"); | ||
141 | 150 | ||
142 | err = copy_from_user(&fdata, uarg, sizeof(struct pps_fdata)); | 151 | err = copy_from_user(&fdata, uarg, sizeof(struct pps_fdata)); |
143 | if (err) | 152 | if (err) |
144 | return -EFAULT; | 153 | return -EFAULT; |
145 | 154 | ||
146 | pps->go = 0; | 155 | ev = pps->last_ev; |
147 | 156 | ||
148 | /* Manage the timeout */ | 157 | /* Manage the timeout */ |
149 | if (fdata.timeout.flags & PPS_TIME_INVALID) | 158 | if (fdata.timeout.flags & PPS_TIME_INVALID) |
150 | err = wait_event_interruptible(pps->queue, pps->go); | 159 | err = wait_event_interruptible(pps->queue, |
160 | ev != pps->last_ev); | ||
151 | else { | 161 | else { |
152 | pr_debug("timeout %lld.%09d\n", | 162 | unsigned long ticks; |
163 | |||
164 | dev_dbg(pps->dev, "timeout %lld.%09d\n", | ||
153 | (long long) fdata.timeout.sec, | 165 | (long long) fdata.timeout.sec, |
154 | fdata.timeout.nsec); | 166 | fdata.timeout.nsec); |
155 | ticks = fdata.timeout.sec * HZ; | 167 | ticks = fdata.timeout.sec * HZ; |
@@ -157,7 +169,9 @@ static long pps_cdev_ioctl(struct file *file, | |||
157 | 169 | ||
158 | if (ticks != 0) { | 170 | if (ticks != 0) { |
159 | err = wait_event_interruptible_timeout( | 171 | err = wait_event_interruptible_timeout( |
160 | pps->queue, pps->go, ticks); | 172 | pps->queue, |
173 | ev != pps->last_ev, | ||
174 | ticks); | ||
161 | if (err == 0) | 175 | if (err == 0) |
162 | return -ETIMEDOUT; | 176 | return -ETIMEDOUT; |
163 | } | 177 | } |
@@ -165,7 +179,7 @@ static long pps_cdev_ioctl(struct file *file, | |||
165 | 179 | ||
166 | /* Check for pending signals */ | 180 | /* Check for pending signals */ |
167 | if (err == -ERESTARTSYS) { | 181 | if (err == -ERESTARTSYS) { |
168 | pr_debug("pending signal caught\n"); | 182 | dev_dbg(pps->dev, "pending signal caught\n"); |
169 | return -EINTR; | 183 | return -EINTR; |
170 | } | 184 | } |
171 | 185 | ||
@@ -185,10 +199,44 @@ static long pps_cdev_ioctl(struct file *file, | |||
185 | return -EFAULT; | 199 | return -EFAULT; |
186 | 200 | ||
187 | break; | 201 | break; |
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; | ||
188 | 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 | } | ||
189 | default: | 238 | default: |
190 | return -ENOTTY; | 239 | return -ENOTTY; |
191 | break; | ||
192 | } | 240 | } |
193 | 241 | ||
194 | return 0; | 242 | return 0; |
@@ -198,12 +246,6 @@ static int pps_cdev_open(struct inode *inode, struct file *file) | |||
198 | { | 246 | { |
199 | struct pps_device *pps = container_of(inode->i_cdev, | 247 | struct pps_device *pps = container_of(inode->i_cdev, |
200 | struct pps_device, cdev); | 248 | struct pps_device, cdev); |
201 | int found; | ||
202 | |||
203 | found = pps_get_source(pps->id) != 0; | ||
204 | if (!found) | ||
205 | return -ENODEV; | ||
206 | |||
207 | file->private_data = pps; | 249 | file->private_data = pps; |
208 | 250 | ||
209 | return 0; | 251 | return 0; |
@@ -211,11 +253,6 @@ static int pps_cdev_open(struct inode *inode, struct file *file) | |||
211 | 253 | ||
212 | static int pps_cdev_release(struct inode *inode, struct file *file) | 254 | static int pps_cdev_release(struct inode *inode, struct file *file) |
213 | { | 255 | { |
214 | struct pps_device *pps = file->private_data; | ||
215 | |||
216 | /* Free the PPS source and wake up (possible) deregistration */ | ||
217 | pps_put_source(pps); | ||
218 | |||
219 | return 0; | 256 | return 0; |
220 | } | 257 | } |
221 | 258 | ||
@@ -233,25 +270,67 @@ static const struct file_operations pps_cdev_fops = { | |||
233 | .release = pps_cdev_release, | 270 | .release = pps_cdev_release, |
234 | }; | 271 | }; |
235 | 272 | ||
273 | static void pps_device_destruct(struct device *dev) | ||
274 | { | ||
275 | struct pps_device *pps = dev_get_drvdata(dev); | ||
276 | |||
277 | /* release id here to protect others from using it while it's | ||
278 | * still in use */ | ||
279 | mutex_lock(&pps_idr_lock); | ||
280 | idr_remove(&pps_idr, pps->id); | ||
281 | mutex_unlock(&pps_idr_lock); | ||
282 | |||
283 | kfree(dev); | ||
284 | kfree(pps); | ||
285 | } | ||
286 | |||
236 | int pps_register_cdev(struct pps_device *pps) | 287 | int pps_register_cdev(struct pps_device *pps) |
237 | { | 288 | { |
238 | int err; | 289 | int err; |
290 | dev_t devt; | ||
291 | |||
292 | mutex_lock(&pps_idr_lock); | ||
293 | /* Get new ID for the new PPS source */ | ||
294 | if (idr_pre_get(&pps_idr, GFP_KERNEL) == 0) { | ||
295 | mutex_unlock(&pps_idr_lock); | ||
296 | return -ENOMEM; | ||
297 | } | ||
298 | |||
299 | /* Now really allocate the PPS source. | ||
300 | * After idr_get_new() calling the new source will be freely available | ||
301 | * into the kernel. | ||
302 | */ | ||
303 | err = idr_get_new(&pps_idr, pps, &pps->id); | ||
304 | mutex_unlock(&pps_idr_lock); | ||
305 | |||
306 | if (err < 0) | ||
307 | return err; | ||
308 | |||
309 | pps->id &= MAX_ID_MASK; | ||
310 | if (pps->id >= PPS_MAX_SOURCES) { | ||
311 | pr_err("%s: too many PPS sources in the system\n", | ||
312 | pps->info.name); | ||
313 | err = -EBUSY; | ||
314 | goto free_idr; | ||
315 | } | ||
316 | |||
317 | devt = MKDEV(MAJOR(pps_devt), pps->id); | ||
239 | 318 | ||
240 | pps->devno = MKDEV(MAJOR(pps_devt), pps->id); | ||
241 | cdev_init(&pps->cdev, &pps_cdev_fops); | 319 | cdev_init(&pps->cdev, &pps_cdev_fops); |
242 | pps->cdev.owner = pps->info.owner; | 320 | pps->cdev.owner = pps->info.owner; |
243 | 321 | ||
244 | err = cdev_add(&pps->cdev, pps->devno, 1); | 322 | err = cdev_add(&pps->cdev, devt, 1); |
245 | if (err) { | 323 | if (err) { |
246 | printk(KERN_ERR "pps: %s: failed to add char device %d:%d\n", | 324 | pr_err("%s: failed to add char device %d:%d\n", |
247 | pps->info.name, MAJOR(pps_devt), pps->id); | 325 | pps->info.name, MAJOR(pps_devt), pps->id); |
248 | return err; | 326 | goto free_idr; |
249 | } | 327 | } |
250 | pps->dev = device_create(pps_class, pps->info.dev, pps->devno, NULL, | 328 | pps->dev = device_create(pps_class, pps->info.dev, devt, pps, |
251 | "pps%d", pps->id); | 329 | "pps%d", pps->id); |
252 | if (IS_ERR(pps->dev)) | 330 | if (IS_ERR(pps->dev)) |
253 | goto del_cdev; | 331 | goto del_cdev; |
254 | dev_set_drvdata(pps->dev, pps); | 332 | |
333 | pps->dev->release = pps_device_destruct; | ||
255 | 334 | ||
256 | pr_debug("source %s got cdev (%d:%d)\n", pps->info.name, | 335 | pr_debug("source %s got cdev (%d:%d)\n", pps->info.name, |
257 | MAJOR(pps_devt), pps->id); | 336 | MAJOR(pps_devt), pps->id); |
@@ -261,12 +340,17 @@ int pps_register_cdev(struct pps_device *pps) | |||
261 | del_cdev: | 340 | del_cdev: |
262 | cdev_del(&pps->cdev); | 341 | cdev_del(&pps->cdev); |
263 | 342 | ||
343 | free_idr: | ||
344 | mutex_lock(&pps_idr_lock); | ||
345 | idr_remove(&pps_idr, pps->id); | ||
346 | mutex_unlock(&pps_idr_lock); | ||
347 | |||
264 | return err; | 348 | return err; |
265 | } | 349 | } |
266 | 350 | ||
267 | void pps_unregister_cdev(struct pps_device *pps) | 351 | void pps_unregister_cdev(struct pps_device *pps) |
268 | { | 352 | { |
269 | device_destroy(pps_class, pps->devno); | 353 | device_destroy(pps_class, pps->dev->devt); |
270 | cdev_del(&pps->cdev); | 354 | cdev_del(&pps->cdev); |
271 | } | 355 | } |
272 | 356 | ||
@@ -286,14 +370,14 @@ static int __init pps_init(void) | |||
286 | 370 | ||
287 | pps_class = class_create(THIS_MODULE, "pps"); | 371 | pps_class = class_create(THIS_MODULE, "pps"); |
288 | if (!pps_class) { | 372 | if (!pps_class) { |
289 | printk(KERN_ERR "pps: failed to allocate class\n"); | 373 | pr_err("failed to allocate class\n"); |
290 | return -ENOMEM; | 374 | return -ENOMEM; |
291 | } | 375 | } |
292 | pps_class->dev_attrs = pps_attrs; | 376 | pps_class->dev_attrs = pps_attrs; |
293 | 377 | ||
294 | err = alloc_chrdev_region(&pps_devt, 0, PPS_MAX_SOURCES, "pps"); | 378 | err = alloc_chrdev_region(&pps_devt, 0, PPS_MAX_SOURCES, "pps"); |
295 | if (err < 0) { | 379 | if (err < 0) { |
296 | printk(KERN_ERR "pps: failed to allocate char device region\n"); | 380 | pr_err("failed to allocate char device region\n"); |
297 | goto remove_class; | 381 | goto remove_class; |
298 | } | 382 | } |
299 | 383 | ||