xref: /linux-6.15/drivers/pps/pps.c (revision 3003d55b)
1 /*
2  * PPS core file
3  *
4  *
5  * Copyright (C) 2005-2009   Rodolfo Giometti <[email protected]>
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 #include <linux/kernel.h>
24 #include <linux/module.h>
25 #include <linux/init.h>
26 #include <linux/sched.h>
27 #include <linux/uaccess.h>
28 #include <linux/idr.h>
29 #include <linux/cdev.h>
30 #include <linux/poll.h>
31 #include <linux/pps_kernel.h>
32 
33 /*
34  * Local variables
35  */
36 
37 static dev_t pps_devt;
38 static struct class *pps_class;
39 
40 /*
41  * Char device methods
42  */
43 
44 static unsigned int pps_cdev_poll(struct file *file, poll_table *wait)
45 {
46 	struct pps_device *pps = file->private_data;
47 
48 	poll_wait(file, &pps->queue, wait);
49 
50 	return POLLIN | POLLRDNORM;
51 }
52 
53 static int pps_cdev_fasync(int fd, struct file *file, int on)
54 {
55 	struct pps_device *pps = file->private_data;
56 	return fasync_helper(fd, file, on, &pps->async_queue);
57 }
58 
59 static long pps_cdev_ioctl(struct file *file,
60 		unsigned int cmd, unsigned long arg)
61 {
62 	struct pps_device *pps = file->private_data;
63 	struct pps_kparams params;
64 	void __user *uarg = (void __user *) arg;
65 	int __user *iuarg = (int __user *) arg;
66 	int err;
67 
68 	switch (cmd) {
69 	case PPS_GETPARAMS:
70 		pr_debug("PPS_GETPARAMS: source %d\n", pps->id);
71 
72 		spin_lock_irq(&pps->lock);
73 
74 		/* Get the current parameters */
75 		params = pps->params;
76 
77 		spin_unlock_irq(&pps->lock);
78 
79 		err = copy_to_user(uarg, &params, sizeof(struct pps_kparams));
80 		if (err)
81 			return -EFAULT;
82 
83 		break;
84 
85 	case PPS_SETPARAMS:
86 		pr_debug("PPS_SETPARAMS: source %d\n", pps->id);
87 
88 		/* Check the capabilities */
89 		if (!capable(CAP_SYS_TIME))
90 			return -EPERM;
91 
92 		err = copy_from_user(&params, uarg, sizeof(struct pps_kparams));
93 		if (err)
94 			return -EFAULT;
95 		if (!(params.mode & (PPS_CAPTUREASSERT | PPS_CAPTURECLEAR))) {
96 			pr_debug("capture mode unspecified (%x)\n",
97 								params.mode);
98 			return -EINVAL;
99 		}
100 
101 		/* Check for supported capabilities */
102 		if ((params.mode & ~pps->info.mode) != 0) {
103 			pr_debug("unsupported capabilities (%x)\n",
104 								params.mode);
105 			return -EINVAL;
106 		}
107 
108 		spin_lock_irq(&pps->lock);
109 
110 		/* Save the new parameters */
111 		pps->params = params;
112 
113 		/* Restore the read only parameters */
114 		if ((params.mode & (PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP)) == 0) {
115 			/* section 3.3 of RFC 2783 interpreted */
116 			pr_debug("time format unspecified (%x)\n",
117 								params.mode);
118 			pps->params.mode |= PPS_TSFMT_TSPEC;
119 		}
120 		if (pps->info.mode & PPS_CANWAIT)
121 			pps->params.mode |= PPS_CANWAIT;
122 		pps->params.api_version = PPS_API_VERS;
123 
124 		spin_unlock_irq(&pps->lock);
125 
126 		break;
127 
128 	case PPS_GETCAP:
129 		pr_debug("PPS_GETCAP: source %d\n", pps->id);
130 
131 		err = put_user(pps->info.mode, iuarg);
132 		if (err)
133 			return -EFAULT;
134 
135 		break;
136 
137 	case PPS_FETCH: {
138 		struct pps_fdata fdata;
139 		unsigned int ev;
140 
141 		pr_debug("PPS_FETCH: source %d\n", pps->id);
142 
143 		err = copy_from_user(&fdata, uarg, sizeof(struct pps_fdata));
144 		if (err)
145 			return -EFAULT;
146 
147 		ev = pps->last_ev;
148 
149 		/* Manage the timeout */
150 		if (fdata.timeout.flags & PPS_TIME_INVALID)
151 			err = wait_event_interruptible(pps->queue,
152 					ev != pps->last_ev);
153 		else {
154 			unsigned long ticks;
155 
156 			pr_debug("timeout %lld.%09d\n",
157 					(long long) fdata.timeout.sec,
158 					fdata.timeout.nsec);
159 			ticks = fdata.timeout.sec * HZ;
160 			ticks += fdata.timeout.nsec / (NSEC_PER_SEC / HZ);
161 
162 			if (ticks != 0) {
163 				err = wait_event_interruptible_timeout(
164 						pps->queue,
165 						ev != pps->last_ev,
166 						ticks);
167 				if (err == 0)
168 					return -ETIMEDOUT;
169 			}
170 		}
171 
172 		/* Check for pending signals */
173 		if (err == -ERESTARTSYS) {
174 			pr_debug("pending signal caught\n");
175 			return -EINTR;
176 		}
177 
178 		/* Return the fetched timestamp */
179 		spin_lock_irq(&pps->lock);
180 
181 		fdata.info.assert_sequence = pps->assert_sequence;
182 		fdata.info.clear_sequence = pps->clear_sequence;
183 		fdata.info.assert_tu = pps->assert_tu;
184 		fdata.info.clear_tu = pps->clear_tu;
185 		fdata.info.current_mode = pps->current_mode;
186 
187 		spin_unlock_irq(&pps->lock);
188 
189 		err = copy_to_user(uarg, &fdata, sizeof(struct pps_fdata));
190 		if (err)
191 			return -EFAULT;
192 
193 		break;
194 	}
195 	default:
196 		return -ENOTTY;
197 		break;
198 	}
199 
200 	return 0;
201 }
202 
203 static int pps_cdev_open(struct inode *inode, struct file *file)
204 {
205 	struct pps_device *pps = container_of(inode->i_cdev,
206 						struct pps_device, cdev);
207 	int found;
208 
209 	found = pps_get_source(pps->id) != 0;
210 	if (!found)
211 		return -ENODEV;
212 
213 	file->private_data = pps;
214 
215 	return 0;
216 }
217 
218 static int pps_cdev_release(struct inode *inode, struct file *file)
219 {
220 	struct pps_device *pps = file->private_data;
221 
222 	/* Free the PPS source and wake up (possible) deregistration */
223 	pps_put_source(pps);
224 
225 	return 0;
226 }
227 
228 /*
229  * Char device stuff
230  */
231 
232 static const struct file_operations pps_cdev_fops = {
233 	.owner		= THIS_MODULE,
234 	.llseek		= no_llseek,
235 	.poll		= pps_cdev_poll,
236 	.fasync		= pps_cdev_fasync,
237 	.unlocked_ioctl	= pps_cdev_ioctl,
238 	.open		= pps_cdev_open,
239 	.release	= pps_cdev_release,
240 };
241 
242 int pps_register_cdev(struct pps_device *pps)
243 {
244 	int err;
245 
246 	pps->devno = MKDEV(MAJOR(pps_devt), pps->id);
247 	cdev_init(&pps->cdev, &pps_cdev_fops);
248 	pps->cdev.owner = pps->info.owner;
249 
250 	err = cdev_add(&pps->cdev, pps->devno, 1);
251 	if (err) {
252 		printk(KERN_ERR "pps: %s: failed to add char device %d:%d\n",
253 				pps->info.name, MAJOR(pps_devt), pps->id);
254 		return err;
255 	}
256 	pps->dev = device_create(pps_class, pps->info.dev, pps->devno, NULL,
257 							"pps%d", pps->id);
258 	if (IS_ERR(pps->dev))
259 		goto del_cdev;
260 	dev_set_drvdata(pps->dev, pps);
261 
262 	pr_debug("source %s got cdev (%d:%d)\n", pps->info.name,
263 			MAJOR(pps_devt), pps->id);
264 
265 	return 0;
266 
267 del_cdev:
268 	cdev_del(&pps->cdev);
269 
270 	return err;
271 }
272 
273 void pps_unregister_cdev(struct pps_device *pps)
274 {
275 	device_destroy(pps_class, pps->devno);
276 	cdev_del(&pps->cdev);
277 }
278 
279 /*
280  * Module stuff
281  */
282 
283 static void __exit pps_exit(void)
284 {
285 	class_destroy(pps_class);
286 	unregister_chrdev_region(pps_devt, PPS_MAX_SOURCES);
287 }
288 
289 static int __init pps_init(void)
290 {
291 	int err;
292 
293 	pps_class = class_create(THIS_MODULE, "pps");
294 	if (!pps_class) {
295 		printk(KERN_ERR "pps: failed to allocate class\n");
296 		return -ENOMEM;
297 	}
298 	pps_class->dev_attrs = pps_attrs;
299 
300 	err = alloc_chrdev_region(&pps_devt, 0, PPS_MAX_SOURCES, "pps");
301 	if (err < 0) {
302 		printk(KERN_ERR "pps: failed to allocate char device region\n");
303 		goto remove_class;
304 	}
305 
306 	pr_info("LinuxPPS API ver. %d registered\n", PPS_API_VERS);
307 	pr_info("Software ver. %s - Copyright 2005-2007 Rodolfo Giometti "
308 		"<[email protected]>\n", PPS_VERSION);
309 
310 	return 0;
311 
312 remove_class:
313 	class_destroy(pps_class);
314 
315 	return err;
316 }
317 
318 subsys_initcall(pps_init);
319 module_exit(pps_exit);
320 
321 MODULE_AUTHOR("Rodolfo Giometti <[email protected]>");
322 MODULE_DESCRIPTION("LinuxPPS support (RFC 2783) - ver. " PPS_VERSION);
323 MODULE_LICENSE("GPL");
324