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