linux/drivers/pps/kapi.c
<<
>>
Prefs
   1/*
   2 * kernel API
   3 *
   4 *
   5 * Copyright (C) 2005-2009   Rodolfo Giometti <giometti@linux.it>
   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#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  23
  24#include <linux/kernel.h>
  25#include <linux/module.h>
  26#include <linux/init.h>
  27#include <linux/sched.h>
  28#include <linux/time.h>
  29#include <linux/timex.h>
  30#include <linux/spinlock.h>
  31#include <linux/fs.h>
  32#include <linux/pps_kernel.h>
  33#include <linux/slab.h>
  34
  35#include "kc.h"
  36
  37/*
  38 * Local functions
  39 */
  40
  41static void pps_add_offset(struct pps_ktime *ts, struct pps_ktime *offset)
  42{
  43        ts->nsec += offset->nsec;
  44        while (ts->nsec >= NSEC_PER_SEC) {
  45                ts->nsec -= NSEC_PER_SEC;
  46                ts->sec++;
  47        }
  48        while (ts->nsec < 0) {
  49                ts->nsec += NSEC_PER_SEC;
  50                ts->sec--;
  51        }
  52        ts->sec += offset->sec;
  53}
  54
  55/*
  56 * Exported functions
  57 */
  58
  59/* pps_register_source - add a PPS source in the system
  60 * @info: the PPS info struct
  61 * @default_params: the default PPS parameters of the new source
  62 *
  63 * This function is used to add a new PPS source in the system. The new
  64 * source is described by info's fields and it will have, as default PPS
  65 * parameters, the ones specified into default_params.
  66 *
  67 * The function returns, in case of success, the PPS device. Otherwise NULL.
  68 */
  69
  70struct pps_device *pps_register_source(struct pps_source_info *info,
  71                int default_params)
  72{
  73        struct pps_device *pps;
  74        int err;
  75
  76        /* Sanity checks */
  77        if ((info->mode & default_params) != default_params) {
  78                pr_err("%s: unsupported default parameters\n",
  79                                        info->name);
  80                err = -EINVAL;
  81                goto pps_register_source_exit;
  82        }
  83        if ((info->mode & (PPS_ECHOASSERT | PPS_ECHOCLEAR)) != 0 &&
  84                        info->echo == NULL) {
  85                pr_err("%s: echo function is not defined\n",
  86                                        info->name);
  87                err = -EINVAL;
  88                goto pps_register_source_exit;
  89        }
  90        if ((info->mode & (PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP)) == 0) {
  91                pr_err("%s: unspecified time format\n",
  92                                        info->name);
  93                err = -EINVAL;
  94                goto pps_register_source_exit;
  95        }
  96
  97        /* Allocate memory for the new PPS source struct */
  98        pps = kzalloc(sizeof(struct pps_device), GFP_KERNEL);
  99        if (pps == NULL) {
 100                err = -ENOMEM;
 101                goto pps_register_source_exit;
 102        }
 103
 104        /* These initializations must be done before calling idr_get_new()
 105         * in order to avoid reces into pps_event().
 106         */
 107        pps->params.api_version = PPS_API_VERS;
 108        pps->params.mode = default_params;
 109        pps->info = *info;
 110
 111        init_waitqueue_head(&pps->queue);
 112        spin_lock_init(&pps->lock);
 113
 114        /* Create the char device */
 115        err = pps_register_cdev(pps);
 116        if (err < 0) {
 117                pr_err("%s: unable to create char device\n",
 118                                        info->name);
 119                goto kfree_pps;
 120        }
 121
 122        dev_info(pps->dev, "new PPS source %s\n", info->name);
 123
 124        return pps;
 125
 126kfree_pps:
 127        kfree(pps);
 128
 129pps_register_source_exit:
 130        pr_err("%s: unable to register source\n", info->name);
 131
 132        return NULL;
 133}
 134EXPORT_SYMBOL(pps_register_source);
 135
 136/* pps_unregister_source - remove a PPS source from the system
 137 * @pps: the PPS source
 138 *
 139 * This function is used to remove a previously registered PPS source from
 140 * the system.
 141 */
 142
 143void pps_unregister_source(struct pps_device *pps)
 144{
 145        pps_kc_remove(pps);
 146        pps_unregister_cdev(pps);
 147
 148        /* don't have to kfree(pps) here because it will be done on
 149         * device destruction */
 150}
 151EXPORT_SYMBOL(pps_unregister_source);
 152
 153/* pps_event - register a PPS event into the system
 154 * @pps: the PPS device
 155 * @ts: the event timestamp
 156 * @event: the event type
 157 * @data: userdef pointer
 158 *
 159 * This function is used by each PPS client in order to register a new
 160 * PPS event into the system (it's usually called inside an IRQ handler).
 161 *
 162 * If an echo function is associated with the PPS device it will be called
 163 * as:
 164 *      pps->info.echo(pps, event, data);
 165 */
 166void pps_event(struct pps_device *pps, struct pps_event_time *ts, int event,
 167                void *data)
 168{
 169        unsigned long flags;
 170        int captured = 0;
 171        struct pps_ktime ts_real = { .sec = 0, .nsec = 0, .flags = 0 };
 172
 173        /* check event type */
 174        BUG_ON((event & (PPS_CAPTUREASSERT | PPS_CAPTURECLEAR)) == 0);
 175
 176        dev_dbg(pps->dev, "PPS event at %ld.%09ld\n",
 177                        ts->ts_real.tv_sec, ts->ts_real.tv_nsec);
 178
 179        timespec_to_pps_ktime(&ts_real, ts->ts_real);
 180
 181        spin_lock_irqsave(&pps->lock, flags);
 182
 183        /* Must call the echo function? */
 184        if ((pps->params.mode & (PPS_ECHOASSERT | PPS_ECHOCLEAR)))
 185                pps->info.echo(pps, event, data);
 186
 187        /* Check the event */
 188        pps->current_mode = pps->params.mode;
 189        if (event & pps->params.mode & PPS_CAPTUREASSERT) {
 190                /* We have to add an offset? */
 191                if (pps->params.mode & PPS_OFFSETASSERT)
 192                        pps_add_offset(&ts_real,
 193                                        &pps->params.assert_off_tu);
 194
 195                /* Save the time stamp */
 196                pps->assert_tu = ts_real;
 197                pps->assert_sequence++;
 198                dev_dbg(pps->dev, "capture assert seq #%u\n",
 199                        pps->assert_sequence);
 200
 201                captured = ~0;
 202        }
 203        if (event & pps->params.mode & PPS_CAPTURECLEAR) {
 204                /* We have to add an offset? */
 205                if (pps->params.mode & PPS_OFFSETCLEAR)
 206                        pps_add_offset(&ts_real,
 207                                        &pps->params.clear_off_tu);
 208
 209                /* Save the time stamp */
 210                pps->clear_tu = ts_real;
 211                pps->clear_sequence++;
 212                dev_dbg(pps->dev, "capture clear seq #%u\n",
 213                        pps->clear_sequence);
 214
 215                captured = ~0;
 216        }
 217
 218        pps_kc_event(pps, ts, event);
 219
 220        /* Wake up if captured something */
 221        if (captured) {
 222                pps->last_ev++;
 223                wake_up_interruptible_all(&pps->queue);
 224
 225                kill_fasync(&pps->async_queue, SIGIO, POLL_IN);
 226        }
 227
 228        spin_unlock_irqrestore(&pps->lock, flags);
 229}
 230EXPORT_SYMBOL(pps_event);
 231
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.