brintos

brintos / linux-shallow public Read only

0
0
Text · 5.4 KiB · d9d566f Raw
224 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * kernel API4 *5 * Copyright (C) 2005-2009   Rodolfo Giometti <giometti@linux.it>6 */7 8#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt9 10#include <linux/kernel.h>11#include <linux/module.h>12#include <linux/init.h>13#include <linux/sched.h>14#include <linux/time.h>15#include <linux/timex.h>16#include <linux/spinlock.h>17#include <linux/fs.h>18#include <linux/pps_kernel.h>19#include <linux/slab.h>20 21#include "kc.h"22 23/*24 * Local functions25 */26 27static void pps_add_offset(struct pps_ktime *ts, struct pps_ktime *offset)28{29	ts->nsec += offset->nsec;30	while (ts->nsec >= NSEC_PER_SEC) {31		ts->nsec -= NSEC_PER_SEC;32		ts->sec++;33	}34	while (ts->nsec < 0) {35		ts->nsec += NSEC_PER_SEC;36		ts->sec--;37	}38	ts->sec += offset->sec;39}40 41static void pps_echo_client_default(struct pps_device *pps, int event,42		void *data)43{44	dev_info(pps->dev, "echo %s %s\n",45		event & PPS_CAPTUREASSERT ? "assert" : "",46		event & PPS_CAPTURECLEAR ? "clear" : "");47}48 49/*50 * Exported functions51 */52 53/* pps_register_source - add a PPS source in the system54 * @info: the PPS info struct55 * @default_params: the default PPS parameters of the new source56 *57 * This function is used to add a new PPS source in the system. The new58 * source is described by info's fields and it will have, as default PPS59 * parameters, the ones specified into default_params.60 *61 * The function returns, in case of success, the PPS device. Otherwise62 * ERR_PTR(errno).63 */64 65struct pps_device *pps_register_source(struct pps_source_info *info,66		int default_params)67{68	struct pps_device *pps;69	int err;70 71	/* Sanity checks */72	if ((info->mode & default_params) != default_params) {73		pr_err("%s: unsupported default parameters\n",74					info->name);75		err = -EINVAL;76		goto pps_register_source_exit;77	}78	if ((info->mode & (PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP)) == 0) {79		pr_err("%s: unspecified time format\n",80					info->name);81		err = -EINVAL;82		goto pps_register_source_exit;83	}84 85	/* Allocate memory for the new PPS source struct */86	pps = kzalloc(sizeof(struct pps_device), GFP_KERNEL);87	if (pps == NULL) {88		err = -ENOMEM;89		goto pps_register_source_exit;90	}91 92	/* These initializations must be done before calling idr_alloc()93	 * in order to avoid reces into pps_event().94	 */95	pps->params.api_version = PPS_API_VERS;96	pps->params.mode = default_params;97	pps->info = *info;98 99	/* check for default echo function */100	if ((pps->info.mode & (PPS_ECHOASSERT | PPS_ECHOCLEAR)) &&101			pps->info.echo == NULL)102		pps->info.echo = pps_echo_client_default;103 104	init_waitqueue_head(&pps->queue);105	spin_lock_init(&pps->lock);106 107	/* Create the char device */108	err = pps_register_cdev(pps);109	if (err < 0) {110		pr_err("%s: unable to create char device\n",111					info->name);112		goto kfree_pps;113	}114 115	dev_info(pps->dev, "new PPS source %s\n", info->name);116 117	return pps;118 119kfree_pps:120	kfree(pps);121 122pps_register_source_exit:123	pr_err("%s: unable to register source\n", info->name);124 125	return ERR_PTR(err);126}127EXPORT_SYMBOL(pps_register_source);128 129/* pps_unregister_source - remove a PPS source from the system130 * @pps: the PPS source131 *132 * This function is used to remove a previously registered PPS source from133 * the system.134 */135 136void pps_unregister_source(struct pps_device *pps)137{138	pps_kc_remove(pps);139	pps_unregister_cdev(pps);140 141	/* don't have to kfree(pps) here because it will be done on142	 * device destruction */143}144EXPORT_SYMBOL(pps_unregister_source);145 146/* pps_event - register a PPS event into the system147 * @pps: the PPS device148 * @ts: the event timestamp149 * @event: the event type150 * @data: userdef pointer151 *152 * This function is used by each PPS client in order to register a new153 * PPS event into the system (it's usually called inside an IRQ handler).154 *155 * If an echo function is associated with the PPS device it will be called156 * as:157 *	pps->info.echo(pps, event, data);158 */159void pps_event(struct pps_device *pps, struct pps_event_time *ts, int event,160		void *data)161{162	unsigned long flags;163	int captured = 0;164	struct pps_ktime ts_real = { .sec = 0, .nsec = 0, .flags = 0 };165 166	/* check event type */167	BUG_ON((event & (PPS_CAPTUREASSERT | PPS_CAPTURECLEAR)) == 0);168 169	dev_dbg(pps->dev, "PPS event at %lld.%09ld\n",170			(s64)ts->ts_real.tv_sec, ts->ts_real.tv_nsec);171 172	timespec_to_pps_ktime(&ts_real, ts->ts_real);173 174	spin_lock_irqsave(&pps->lock, flags);175 176	/* Must call the echo function? */177	if ((pps->params.mode & (PPS_ECHOASSERT | PPS_ECHOCLEAR)))178		pps->info.echo(pps, event, data);179 180	/* Check the event */181	pps->current_mode = pps->params.mode;182	if (event & pps->params.mode & PPS_CAPTUREASSERT) {183		/* We have to add an offset? */184		if (pps->params.mode & PPS_OFFSETASSERT)185			pps_add_offset(&ts_real,186					&pps->params.assert_off_tu);187 188		/* Save the time stamp */189		pps->assert_tu = ts_real;190		pps->assert_sequence++;191		dev_dbg(pps->dev, "capture assert seq #%u\n",192			pps->assert_sequence);193 194		captured = ~0;195	}196	if (event & pps->params.mode & PPS_CAPTURECLEAR) {197		/* We have to add an offset? */198		if (pps->params.mode & PPS_OFFSETCLEAR)199			pps_add_offset(&ts_real,200					&pps->params.clear_off_tu);201 202		/* Save the time stamp */203		pps->clear_tu = ts_real;204		pps->clear_sequence++;205		dev_dbg(pps->dev, "capture clear seq #%u\n",206			pps->clear_sequence);207 208		captured = ~0;209	}210 211	pps_kc_event(pps, ts, event);212 213	/* Wake up if captured something */214	if (captured) {215		pps->last_ev++;216		wake_up_interruptible_all(&pps->queue);217 218		kill_fasync(&pps->async_queue, SIGIO, POLL_IN);219	}220 221	spin_unlock_irqrestore(&pps->lock, flags);222}223EXPORT_SYMBOL(pps_event);224