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