Blame view

drivers/pps/kapi.c 6.03 KB
eae9d2ba0   Rodolfo Giometti   LinuxPPS: core su...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
  /*
   * kernel API
   *
   *
   * Copyright (C) 2005-2009   Rodolfo Giometti <giometti@linux.it>
   *
   *   This program is free software; you can redistribute it and/or modify
   *   it under the terms of the GNU General Public License as published by
   *   the Free Software Foundation; either version 2 of the License, or
   *   (at your option) any later version.
   *
   *   This program is distributed in the hope that it will be useful,
   *   but WITHOUT ANY WARRANTY; without even the implied warranty of
   *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   *   GNU General Public License for more details.
   *
   *   You should have received a copy of the GNU General Public License
   *   along with this program; if not, write to the Free Software
   *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
   */
7f7cce741   Alexander Gordeev   pps: convert prin...
21
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
eae9d2ba0   Rodolfo Giometti   LinuxPPS: core su...
22
23
24
25
26
27
  
  #include <linux/kernel.h>
  #include <linux/module.h>
  #include <linux/init.h>
  #include <linux/sched.h>
  #include <linux/time.h>
717c03366   Alexander Gordeev   pps: add kernel c...
28
  #include <linux/timex.h>
eae9d2ba0   Rodolfo Giometti   LinuxPPS: core su...
29
  #include <linux/spinlock.h>
eae9d2ba0   Rodolfo Giometti   LinuxPPS: core su...
30
31
  #include <linux/fs.h>
  #include <linux/pps_kernel.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
32
  #include <linux/slab.h>
eae9d2ba0   Rodolfo Giometti   LinuxPPS: core su...
33

717c03366   Alexander Gordeev   pps: add kernel c...
34
  #include "kc.h"
eae9d2ba0   Rodolfo Giometti   LinuxPPS: core su...
35
  /*
eae9d2ba0   Rodolfo Giometti   LinuxPPS: core su...
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
   * Local functions
   */
  
  static void pps_add_offset(struct pps_ktime *ts, struct pps_ktime *offset)
  {
  	ts->nsec += offset->nsec;
  	while (ts->nsec >= NSEC_PER_SEC) {
  		ts->nsec -= NSEC_PER_SEC;
  		ts->sec++;
  	}
  	while (ts->nsec < 0) {
  		ts->nsec += NSEC_PER_SEC;
  		ts->sec--;
  	}
  	ts->sec += offset->sec;
  }
437c53418   James Nuss   pps: default echo...
52
53
54
55
56
57
58
59
  static void pps_echo_client_default(struct pps_device *pps, int event,
  		void *data)
  {
  	dev_info(pps->dev, "echo %s %s
  ",
  		event & PPS_CAPTUREASSERT ? "assert" : "",
  		event & PPS_CAPTURECLEAR ? "clear" : "");
  }
eae9d2ba0   Rodolfo Giometti   LinuxPPS: core su...
60
61
62
  /*
   * Exported functions
   */
eae9d2ba0   Rodolfo Giometti   LinuxPPS: core su...
63
64
65
66
67
68
69
70
  /* pps_register_source - add a PPS source in the system
   * @info: the PPS info struct
   * @default_params: the default PPS parameters of the new source
   *
   * This function is used to add a new PPS source in the system. The new
   * source is described by info's fields and it will have, as default PPS
   * parameters, the ones specified into default_params.
   *
5e196d34a   Alexander Gordeev   pps: access pps d...
71
   * The function returns, in case of success, the PPS device. Otherwise NULL.
eae9d2ba0   Rodolfo Giometti   LinuxPPS: core su...
72
   */
5e196d34a   Alexander Gordeev   pps: access pps d...
73
74
  struct pps_device *pps_register_source(struct pps_source_info *info,
  		int default_params)
eae9d2ba0   Rodolfo Giometti   LinuxPPS: core su...
75
76
  {
  	struct pps_device *pps;
eae9d2ba0   Rodolfo Giometti   LinuxPPS: core su...
77
78
79
80
  	int err;
  
  	/* Sanity checks */
  	if ((info->mode & default_params) != default_params) {
7f7cce741   Alexander Gordeev   pps: convert prin...
81
82
  		pr_err("%s: unsupported default parameters
  ",
eae9d2ba0   Rodolfo Giometti   LinuxPPS: core su...
83
84
85
86
  					info->name);
  		err = -EINVAL;
  		goto pps_register_source_exit;
  	}
eae9d2ba0   Rodolfo Giometti   LinuxPPS: core su...
87
  	if ((info->mode & (PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP)) == 0) {
7f7cce741   Alexander Gordeev   pps: convert prin...
88
89
  		pr_err("%s: unspecified time format
  ",
eae9d2ba0   Rodolfo Giometti   LinuxPPS: core su...
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
  					info->name);
  		err = -EINVAL;
  		goto pps_register_source_exit;
  	}
  
  	/* Allocate memory for the new PPS source struct */
  	pps = kzalloc(sizeof(struct pps_device), GFP_KERNEL);
  	if (pps == NULL) {
  		err = -ENOMEM;
  		goto pps_register_source_exit;
  	}
  
  	/* These initializations must be done before calling idr_get_new()
  	 * in order to avoid reces into pps_event().
  	 */
  	pps->params.api_version = PPS_API_VERS;
  	pps->params.mode = default_params;
  	pps->info = *info;
437c53418   James Nuss   pps: default echo...
108
109
110
111
  	/* check for default echo function */
  	if ((pps->info.mode & (PPS_ECHOASSERT | PPS_ECHOCLEAR)) &&
  			pps->info.echo == NULL)
  		pps->info.echo = pps_echo_client_default;
eae9d2ba0   Rodolfo Giometti   LinuxPPS: core su...
112
113
  	init_waitqueue_head(&pps->queue);
  	spin_lock_init(&pps->lock);
eae9d2ba0   Rodolfo Giometti   LinuxPPS: core su...
114

eae9d2ba0   Rodolfo Giometti   LinuxPPS: core su...
115
116
117
  	/* Create the char device */
  	err = pps_register_cdev(pps);
  	if (err < 0) {
7f7cce741   Alexander Gordeev   pps: convert prin...
118
119
  		pr_err("%s: unable to create char device
  ",
eae9d2ba0   Rodolfo Giometti   LinuxPPS: core su...
120
  					info->name);
083e58666   Alexander Gordeev   pps: move idr stu...
121
  		goto kfree_pps;
eae9d2ba0   Rodolfo Giometti   LinuxPPS: core su...
122
  	}
7f7cce741   Alexander Gordeev   pps: convert prin...
123
124
  	dev_info(pps->dev, "new PPS source %s
  ", info->name);
eae9d2ba0   Rodolfo Giometti   LinuxPPS: core su...
125

5e196d34a   Alexander Gordeev   pps: access pps d...
126
  	return pps;
eae9d2ba0   Rodolfo Giometti   LinuxPPS: core su...
127

eae9d2ba0   Rodolfo Giometti   LinuxPPS: core su...
128
129
130
131
  kfree_pps:
  	kfree(pps);
  
  pps_register_source_exit:
7f7cce741   Alexander Gordeev   pps: convert prin...
132
133
  	pr_err("%s: unable to register source
  ", info->name);
eae9d2ba0   Rodolfo Giometti   LinuxPPS: core su...
134

5e196d34a   Alexander Gordeev   pps: access pps d...
135
  	return NULL;
eae9d2ba0   Rodolfo Giometti   LinuxPPS: core su...
136
137
138
139
  }
  EXPORT_SYMBOL(pps_register_source);
  
  /* pps_unregister_source - remove a PPS source from the system
5e196d34a   Alexander Gordeev   pps: access pps d...
140
   * @pps: the PPS source
eae9d2ba0   Rodolfo Giometti   LinuxPPS: core su...
141
142
143
144
   *
   * This function is used to remove a previously registered PPS source from
   * the system.
   */
5e196d34a   Alexander Gordeev   pps: access pps d...
145
  void pps_unregister_source(struct pps_device *pps)
eae9d2ba0   Rodolfo Giometti   LinuxPPS: core su...
146
  {
717c03366   Alexander Gordeev   pps: add kernel c...
147
  	pps_kc_remove(pps);
5e196d34a   Alexander Gordeev   pps: access pps d...
148
  	pps_unregister_cdev(pps);
eae9d2ba0   Rodolfo Giometti   LinuxPPS: core su...
149

083e58666   Alexander Gordeev   pps: move idr stu...
150
151
  	/* don't have to kfree(pps) here because it will be done on
  	 * device destruction */
eae9d2ba0   Rodolfo Giometti   LinuxPPS: core su...
152
153
154
155
  }
  EXPORT_SYMBOL(pps_unregister_source);
  
  /* pps_event - register a PPS event into the system
5e196d34a   Alexander Gordeev   pps: access pps d...
156
   * @pps: the PPS device
eae9d2ba0   Rodolfo Giometti   LinuxPPS: core su...
157
158
159
160
161
162
163
   * @ts: the event timestamp
   * @event: the event type
   * @data: userdef pointer
   *
   * This function is used by each PPS client in order to register a new
   * PPS event into the system (it's usually called inside an IRQ handler).
   *
5e196d34a   Alexander Gordeev   pps: access pps d...
164
   * If an echo function is associated with the PPS device it will be called
eae9d2ba0   Rodolfo Giometti   LinuxPPS: core su...
165
   * as:
5e196d34a   Alexander Gordeev   pps: access pps d...
166
   *	pps->info.echo(pps, event, data);
eae9d2ba0   Rodolfo Giometti   LinuxPPS: core su...
167
   */
5e196d34a   Alexander Gordeev   pps: access pps d...
168
169
  void pps_event(struct pps_device *pps, struct pps_event_time *ts, int event,
  		void *data)
eae9d2ba0   Rodolfo Giometti   LinuxPPS: core su...
170
  {
eae9d2ba0   Rodolfo Giometti   LinuxPPS: core su...
171
  	unsigned long flags;
276b282e9   Rodolfo Giometti   pps: events repor...
172
  	int captured = 0;
99b0d365e   Alexander Gordeev   pps: initialize t...
173
  	struct pps_ktime ts_real = { .sec = 0, .nsec = 0, .flags = 0 };
eae9d2ba0   Rodolfo Giometti   LinuxPPS: core su...
174

29f347c9f   Alexander Gordeev   pps: use BUG_ON f...
175
176
  	/* check event type */
  	BUG_ON((event & (PPS_CAPTUREASSERT | PPS_CAPTURECLEAR)) == 0);
eae9d2ba0   Rodolfo Giometti   LinuxPPS: core su...
177

5e196d34a   Alexander Gordeev   pps: access pps d...
178
179
180
  	dev_dbg(pps->dev, "PPS event at %ld.%09ld
  ",
  			ts->ts_real.tv_sec, ts->ts_real.tv_nsec);
6f4229b51   Alexander Gordeev   pps: unify timest...
181
182
  
  	timespec_to_pps_ktime(&ts_real, ts->ts_real);
eae9d2ba0   Rodolfo Giometti   LinuxPPS: core su...
183
184
185
186
187
  
  	spin_lock_irqsave(&pps->lock, flags);
  
  	/* Must call the echo function? */
  	if ((pps->params.mode & (PPS_ECHOASSERT | PPS_ECHOCLEAR)))
5e196d34a   Alexander Gordeev   pps: access pps d...
188
  		pps->info.echo(pps, event, data);
eae9d2ba0   Rodolfo Giometti   LinuxPPS: core su...
189
190
191
  
  	/* Check the event */
  	pps->current_mode = pps->params.mode;
818b9eefe   Alexander Gordeev   pps: simplify con...
192
  	if (event & pps->params.mode & PPS_CAPTUREASSERT) {
eae9d2ba0   Rodolfo Giometti   LinuxPPS: core su...
193
194
  		/* We have to add an offset? */
  		if (pps->params.mode & PPS_OFFSETASSERT)
6f4229b51   Alexander Gordeev   pps: unify timest...
195
196
  			pps_add_offset(&ts_real,
  					&pps->params.assert_off_tu);
eae9d2ba0   Rodolfo Giometti   LinuxPPS: core su...
197
198
  
  		/* Save the time stamp */
6f4229b51   Alexander Gordeev   pps: unify timest...
199
  		pps->assert_tu = ts_real;
eae9d2ba0   Rodolfo Giometti   LinuxPPS: core su...
200
  		pps->assert_sequence++;
5e196d34a   Alexander Gordeev   pps: access pps d...
201
202
203
  		dev_dbg(pps->dev, "capture assert seq #%u
  ",
  			pps->assert_sequence);
276b282e9   Rodolfo Giometti   pps: events repor...
204
205
  
  		captured = ~0;
eae9d2ba0   Rodolfo Giometti   LinuxPPS: core su...
206
  	}
818b9eefe   Alexander Gordeev   pps: simplify con...
207
  	if (event & pps->params.mode & PPS_CAPTURECLEAR) {
eae9d2ba0   Rodolfo Giometti   LinuxPPS: core su...
208
209
  		/* We have to add an offset? */
  		if (pps->params.mode & PPS_OFFSETCLEAR)
6f4229b51   Alexander Gordeev   pps: unify timest...
210
211
  			pps_add_offset(&ts_real,
  					&pps->params.clear_off_tu);
eae9d2ba0   Rodolfo Giometti   LinuxPPS: core su...
212
213
  
  		/* Save the time stamp */
6f4229b51   Alexander Gordeev   pps: unify timest...
214
  		pps->clear_tu = ts_real;
eae9d2ba0   Rodolfo Giometti   LinuxPPS: core su...
215
  		pps->clear_sequence++;
5e196d34a   Alexander Gordeev   pps: access pps d...
216
217
218
  		dev_dbg(pps->dev, "capture clear seq #%u
  ",
  			pps->clear_sequence);
276b282e9   Rodolfo Giometti   pps: events repor...
219
220
  
  		captured = ~0;
eae9d2ba0   Rodolfo Giometti   LinuxPPS: core su...
221
  	}
717c03366   Alexander Gordeev   pps: add kernel c...
222
  	pps_kc_event(pps, ts, event);
7a21a3cc0   Alexander Gordeev   pps: trivial fixes
223
  	/* Wake up if captured something */
276b282e9   Rodolfo Giometti   pps: events repor...
224
  	if (captured) {
3003d55b5   Alexander Gordeev   pps: fix race in ...
225
226
  		pps->last_ev++;
  		wake_up_interruptible_all(&pps->queue);
eae9d2ba0   Rodolfo Giometti   LinuxPPS: core su...
227

276b282e9   Rodolfo Giometti   pps: events repor...
228
229
  		kill_fasync(&pps->async_queue, SIGIO, POLL_IN);
  	}
eae9d2ba0   Rodolfo Giometti   LinuxPPS: core su...
230
231
  
  	spin_unlock_irqrestore(&pps->lock, flags);
eae9d2ba0   Rodolfo Giometti   LinuxPPS: core su...
232
233
  }
  EXPORT_SYMBOL(pps_event);