Commit 717c033669ed3ceaee8df57d4562fafcc1a6267a

Authored by Alexander Gordeev
Committed by Linus Torvalds
1 parent e2c18e49a0

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>

Showing 7 changed files with 220 additions and 2 deletions Side-by-side Diff

Documentation/ioctl/ioctl-number.txt
... ... @@ -247,7 +247,7 @@
247 247 'p' 40-7F linux/nvram.h
248 248 'p' 80-9F linux/ppdev.h user-space parport
249 249 <mailto:tim@cyberelk.net>
250   -'p' A1-A4 linux/pps.h LinuxPPS
  250 +'p' A1-A5 linux/pps.h LinuxPPS
251 251 <mailto:giometti@linux.it>
252 252 'q' 00-1F linux/serio.h
253 253 'q' 80-FF linux/telephony.h Internet PhoneJACK, Internet LineJACK
drivers/pps/Makefile
... ... @@ -3,6 +3,7 @@
3 3 #
4 4  
5 5 pps_core-y := pps.o kapi.o sysfs.o
  6 +pps_core-$(CONFIG_NTP_PPS) += kc.o
6 7 obj-$(CONFIG_PPS) := pps_core.o
7 8 obj-y += clients/
8 9  
... ... @@ -26,11 +26,14 @@
26 26 #include <linux/init.h>
27 27 #include <linux/sched.h>
28 28 #include <linux/time.h>
  29 +#include <linux/timex.h>
29 30 #include <linux/spinlock.h>
30 31 #include <linux/fs.h>
31 32 #include <linux/pps_kernel.h>
32 33 #include <linux/slab.h>
33 34  
  35 +#include "kc.h"
  36 +
34 37 /*
35 38 * Local functions
36 39 */
... ... @@ -139,6 +142,7 @@
139 142  
140 143 void pps_unregister_source(struct pps_device *pps)
141 144 {
  145 + pps_kc_remove(pps);
142 146 pps_unregister_cdev(pps);
143 147  
144 148 /* don't have to kfree(pps) here because it will be done on
... ... @@ -210,6 +214,8 @@
210 214  
211 215 captured = ~0;
212 216 }
  217 +
  218 + pps_kc_event(pps, ts, event);
213 219  
214 220 /* Wake up if captured something */
215 221 if (captured) {
  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 +}
  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 */
... ... @@ -33,6 +33,8 @@
33 33 #include <linux/pps_kernel.h>
34 34 #include <linux/slab.h>
35 35  
  36 +#include "kc.h"
  37 +
36 38 /*
37 39 * Local variables
38 40 */
39 41  
... ... @@ -198,9 +200,43 @@
198 200  
199 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 238 default:
202 239 return -ENOTTY;
203   - break;
204 240 }
205 241  
206 242 return 0;
... ... @@ -114,12 +114,19 @@
114 114 struct pps_ktime timeout;
115 115 };
116 116  
  117 +struct 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 123 #include <linux/ioctl.h>
118 124  
119 125 #define PPS_GETPARAMS _IOR('p', 0xa1, struct pps_kparams *)
120 126 #define PPS_SETPARAMS _IOW('p', 0xa2, struct pps_kparams *)
121 127 #define PPS_GETCAP _IOR('p', 0xa3, int *)
122 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 131 #endif /* _PPS_H_ */