Linux Audio

Check our new training course

Loading...
v6.8
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * PPS kernel consumer API
  4 *
  5 * Copyright (C) 2009-2010   Alexander Gordeev <lasaine@lvk.cs.msu.su>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
  6 */
  7
  8#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  9
 10#include <linux/kernel.h>
 11#include <linux/module.h>
 12#include <linux/device.h>
 13#include <linux/init.h>
 14#include <linux/spinlock.h>
 15#include <linux/pps_kernel.h>
 16
 17#include "kc.h"
 18
 19/*
 20 * Global variables
 21 */
 22
 23/* state variables to bind kernel consumer */
 24static DEFINE_SPINLOCK(pps_kc_hardpps_lock);
 25/* PPS API (RFC 2783): current source and mode for kernel consumer */
 26static struct pps_device *pps_kc_hardpps_dev;	/* unique pointer to device */
 27static int pps_kc_hardpps_mode;		/* mode bits for kernel consumer */
 28
 29/* pps_kc_bind - control PPS kernel consumer binding
 30 * @pps: the PPS source
 31 * @bind_args: kernel consumer bind parameters
 32 *
 33 * This function is used to bind or unbind PPS kernel consumer according to
 34 * supplied parameters. Should not be called in interrupt context.
 35 */
 36int pps_kc_bind(struct pps_device *pps, struct pps_bind_args *bind_args)
 37{
 38	/* Check if another consumer is already bound */
 39	spin_lock_irq(&pps_kc_hardpps_lock);
 40
 41	if (bind_args->edge == 0)
 42		if (pps_kc_hardpps_dev == pps) {
 43			pps_kc_hardpps_mode = 0;
 44			pps_kc_hardpps_dev = NULL;
 45			spin_unlock_irq(&pps_kc_hardpps_lock);
 46			dev_info(pps->dev, "unbound kernel"
 47					" consumer\n");
 48		} else {
 49			spin_unlock_irq(&pps_kc_hardpps_lock);
 50			dev_err(pps->dev, "selected kernel consumer"
 51					" is not bound\n");
 52			return -EINVAL;
 53		}
 54	else
 55		if (pps_kc_hardpps_dev == NULL ||
 56				pps_kc_hardpps_dev == pps) {
 57			pps_kc_hardpps_mode = bind_args->edge;
 58			pps_kc_hardpps_dev = pps;
 59			spin_unlock_irq(&pps_kc_hardpps_lock);
 60			dev_info(pps->dev, "bound kernel consumer: "
 61				"edge=0x%x\n", bind_args->edge);
 62		} else {
 63			spin_unlock_irq(&pps_kc_hardpps_lock);
 64			dev_err(pps->dev, "another kernel consumer"
 65					" is already bound\n");
 66			return -EINVAL;
 67		}
 68
 69	return 0;
 70}
 71
 72/* pps_kc_remove - unbind kernel consumer on PPS source removal
 73 * @pps: the PPS source
 74 *
 75 * This function is used to disable kernel consumer on PPS source removal
 76 * if this source was bound to PPS kernel consumer. Can be called on any
 77 * source safely. Should not be called in interrupt context.
 78 */
 79void pps_kc_remove(struct pps_device *pps)
 80{
 81	spin_lock_irq(&pps_kc_hardpps_lock);
 82	if (pps == pps_kc_hardpps_dev) {
 83		pps_kc_hardpps_mode = 0;
 84		pps_kc_hardpps_dev = NULL;
 85		spin_unlock_irq(&pps_kc_hardpps_lock);
 86		dev_info(pps->dev, "unbound kernel consumer"
 87				" on device removal\n");
 88	} else
 89		spin_unlock_irq(&pps_kc_hardpps_lock);
 90}
 91
 92/* pps_kc_event - call hardpps() on PPS event
 93 * @pps: the PPS source
 94 * @ts: PPS event timestamp
 95 * @event: PPS event edge
 96 *
 97 * This function calls hardpps() when an event from bound PPS source occurs.
 98 */
 99void pps_kc_event(struct pps_device *pps, struct pps_event_time *ts,
100		int event)
101{
102	unsigned long flags;
103
104	/* Pass some events to kernel consumer if activated */
105	spin_lock_irqsave(&pps_kc_hardpps_lock, flags);
106	if (pps == pps_kc_hardpps_dev && event & pps_kc_hardpps_mode)
107		hardpps(&ts->ts_real, &ts->ts_raw);
108	spin_unlock_irqrestore(&pps_kc_hardpps_lock, flags);
109}
v3.15
 
  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 */
 37static DEFINE_SPINLOCK(pps_kc_hardpps_lock);
 38/* PPS API (RFC 2783): current source and mode for kernel consumer */
 39static struct pps_device *pps_kc_hardpps_dev;	/* unique pointer to device */
 40static 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 */
 49int 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 */
 92void 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 */
112void 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}