Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Alexander Gordeev and committed by
Linus Torvalds
717c0336 e2c18e49

+220 -2
+1 -1
Documentation/ioctl/ioctl-number.txt
··· 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
+1
drivers/pps/Makefile
··· 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
+6
drivers/pps/kapi.c
··· 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> 34 + 35 + #include "kc.h" 33 36 34 37 /* 35 38 * Local functions ··· 142 139 143 140 void pps_unregister_source(struct pps_device *pps) 144 141 { 142 + pps_kc_remove(pps); 145 143 pps_unregister_cdev(pps); 146 144 147 145 /* don't have to kfree(pps) here because it will be done on ··· 214 210 215 211 captured = ~0; 216 212 } 213 + 214 + pps_kc_event(pps, ts, event); 217 215 218 216 /* Wake up if captured something */ 219 217 if (captured) {
+122
drivers/pps/kc.c
··· 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 + }
+46
drivers/pps/kc.h
··· 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 */
+37 -1
drivers/pps/pps.c
··· 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 */ ··· 200 198 201 199 break; 202 200 } 201 + case PPS_KC_BIND: { 202 + struct pps_bind_args bind_args; 203 + 204 + dev_dbg(pps->dev, "PPS_KC_BIND\n"); 205 + 206 + /* Check the capabilities */ 207 + if (!capable(CAP_SYS_TIME)) 208 + return -EPERM; 209 + 210 + if (copy_from_user(&bind_args, uarg, 211 + sizeof(struct pps_bind_args))) 212 + return -EFAULT; 213 + 214 + /* Check for supported capabilities */ 215 + if ((bind_args.edge & ~pps->info.mode) != 0) { 216 + dev_err(pps->dev, "unsupported capabilities (%x)\n", 217 + bind_args.edge); 218 + return -EINVAL; 219 + } 220 + 221 + /* Validate parameters roughly */ 222 + if (bind_args.tsformat != PPS_TSFMT_TSPEC || 223 + (bind_args.edge & ~PPS_CAPTUREBOTH) != 0 || 224 + bind_args.consumer != PPS_KC_HARDPPS) { 225 + dev_err(pps->dev, "invalid kernel consumer bind" 226 + " parameters (%x)\n", bind_args.edge); 227 + return -EINVAL; 228 + } 229 + 230 + err = pps_kc_bind(pps, &bind_args); 231 + if (err < 0) 232 + return err; 233 + 234 + break; 235 + } 203 236 default: 204 237 return -ENOTTY; 205 - break; 206 238 } 207 239 208 240 return 0;
+7
include/linux/pps.h
··· 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_ */