1ebacd801SBjoern A. Zeeb /*
2ebacd801SBjoern A. Zeeb * Copyright (c) 2012 Neratec Solutions AG
3ebacd801SBjoern A. Zeeb *
4ebacd801SBjoern A. Zeeb * Permission to use, copy, modify, and/or distribute this software for any
5ebacd801SBjoern A. Zeeb * purpose with or without fee is hereby granted, provided that the above
6ebacd801SBjoern A. Zeeb * copyright notice and this permission notice appear in all copies.
7ebacd801SBjoern A. Zeeb *
8ebacd801SBjoern A. Zeeb * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9ebacd801SBjoern A. Zeeb * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10ebacd801SBjoern A. Zeeb * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11ebacd801SBjoern A. Zeeb * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12ebacd801SBjoern A. Zeeb * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13ebacd801SBjoern A. Zeeb * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14ebacd801SBjoern A. Zeeb * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15ebacd801SBjoern A. Zeeb */
16ebacd801SBjoern A. Zeeb
17ebacd801SBjoern A. Zeeb #include <linux/slab.h>
18ebacd801SBjoern A. Zeeb #include <linux/spinlock.h>
19ebacd801SBjoern A. Zeeb
20ebacd801SBjoern A. Zeeb #include "ath.h"
21ebacd801SBjoern A. Zeeb #include "dfs_pattern_detector.h"
22ebacd801SBjoern A. Zeeb #include "dfs_pri_detector.h"
23ebacd801SBjoern A. Zeeb
24ebacd801SBjoern A. Zeeb struct ath_dfs_pool_stats global_dfs_pool_stats = {};
25ebacd801SBjoern A. Zeeb
26ebacd801SBjoern A. Zeeb #define DFS_POOL_STAT_INC(c) (global_dfs_pool_stats.c++)
27ebacd801SBjoern A. Zeeb #define DFS_POOL_STAT_DEC(c) (global_dfs_pool_stats.c--)
28ebacd801SBjoern A. Zeeb #define GET_PRI_TO_USE(MIN, MAX, RUNTIME) \
29ebacd801SBjoern A. Zeeb (MIN + PRI_TOLERANCE == MAX - PRI_TOLERANCE ? \
30ebacd801SBjoern A. Zeeb MIN + PRI_TOLERANCE : RUNTIME)
31ebacd801SBjoern A. Zeeb
32ebacd801SBjoern A. Zeeb /*
33ebacd801SBjoern A. Zeeb * struct pulse_elem - elements in pulse queue
34ebacd801SBjoern A. Zeeb */
35ebacd801SBjoern A. Zeeb struct pulse_elem {
36ebacd801SBjoern A. Zeeb struct list_head head;
37ebacd801SBjoern A. Zeeb u64 ts;
38ebacd801SBjoern A. Zeeb };
39ebacd801SBjoern A. Zeeb
40ebacd801SBjoern A. Zeeb /*
41ebacd801SBjoern A. Zeeb * pde_get_multiple() - get number of multiples considering a given tolerance
42ebacd801SBjoern A. Zeeb * Return value: factor if abs(val - factor*fraction) <= tolerance, 0 otherwise
43ebacd801SBjoern A. Zeeb */
pde_get_multiple(u32 val,u32 fraction,u32 tolerance)44ebacd801SBjoern A. Zeeb static u32 pde_get_multiple(u32 val, u32 fraction, u32 tolerance)
45ebacd801SBjoern A. Zeeb {
46ebacd801SBjoern A. Zeeb u32 remainder;
47ebacd801SBjoern A. Zeeb u32 factor;
48ebacd801SBjoern A. Zeeb u32 delta;
49ebacd801SBjoern A. Zeeb
50ebacd801SBjoern A. Zeeb if (fraction == 0)
51ebacd801SBjoern A. Zeeb return 0;
52ebacd801SBjoern A. Zeeb
53ebacd801SBjoern A. Zeeb delta = (val < fraction) ? (fraction - val) : (val - fraction);
54ebacd801SBjoern A. Zeeb
55ebacd801SBjoern A. Zeeb if (delta <= tolerance)
56ebacd801SBjoern A. Zeeb /* val and fraction are within tolerance */
57ebacd801SBjoern A. Zeeb return 1;
58ebacd801SBjoern A. Zeeb
59ebacd801SBjoern A. Zeeb factor = val / fraction;
60ebacd801SBjoern A. Zeeb remainder = val % fraction;
61ebacd801SBjoern A. Zeeb if (remainder > tolerance) {
62ebacd801SBjoern A. Zeeb /* no exact match */
63ebacd801SBjoern A. Zeeb if ((fraction - remainder) <= tolerance)
64ebacd801SBjoern A. Zeeb /* remainder is within tolerance */
65ebacd801SBjoern A. Zeeb factor++;
66ebacd801SBjoern A. Zeeb else
67ebacd801SBjoern A. Zeeb factor = 0;
68ebacd801SBjoern A. Zeeb }
69ebacd801SBjoern A. Zeeb return factor;
70ebacd801SBjoern A. Zeeb }
71ebacd801SBjoern A. Zeeb
72ebacd801SBjoern A. Zeeb /*
73ebacd801SBjoern A. Zeeb * DOC: Singleton Pulse and Sequence Pools
74ebacd801SBjoern A. Zeeb *
75ebacd801SBjoern A. Zeeb * Instances of pri_sequence and pulse_elem are kept in singleton pools to
76ebacd801SBjoern A. Zeeb * reduce the number of dynamic allocations. They are shared between all
77ebacd801SBjoern A. Zeeb * instances and grow up to the peak number of simultaneously used objects.
78ebacd801SBjoern A. Zeeb *
79ebacd801SBjoern A. Zeeb * Memory is freed after all references to the pools are released.
80ebacd801SBjoern A. Zeeb */
81ebacd801SBjoern A. Zeeb static u32 singleton_pool_references;
82ebacd801SBjoern A. Zeeb static LIST_HEAD(pulse_pool);
83ebacd801SBjoern A. Zeeb static LIST_HEAD(pseq_pool);
84ebacd801SBjoern A. Zeeb static DEFINE_SPINLOCK(pool_lock);
85ebacd801SBjoern A. Zeeb
pool_register_ref(void)86ebacd801SBjoern A. Zeeb static void pool_register_ref(void)
87ebacd801SBjoern A. Zeeb {
88ebacd801SBjoern A. Zeeb spin_lock_bh(&pool_lock);
89ebacd801SBjoern A. Zeeb singleton_pool_references++;
90ebacd801SBjoern A. Zeeb DFS_POOL_STAT_INC(pool_reference);
91ebacd801SBjoern A. Zeeb spin_unlock_bh(&pool_lock);
92ebacd801SBjoern A. Zeeb }
93ebacd801SBjoern A. Zeeb
pool_deregister_ref(void)94ebacd801SBjoern A. Zeeb static void pool_deregister_ref(void)
95ebacd801SBjoern A. Zeeb {
96ebacd801SBjoern A. Zeeb spin_lock_bh(&pool_lock);
97ebacd801SBjoern A. Zeeb singleton_pool_references--;
98ebacd801SBjoern A. Zeeb DFS_POOL_STAT_DEC(pool_reference);
99ebacd801SBjoern A. Zeeb if (singleton_pool_references == 0) {
100ebacd801SBjoern A. Zeeb /* free singleton pools with no references left */
101ebacd801SBjoern A. Zeeb struct pri_sequence *ps, *ps0;
102ebacd801SBjoern A. Zeeb struct pulse_elem *p, *p0;
103ebacd801SBjoern A. Zeeb
104ebacd801SBjoern A. Zeeb list_for_each_entry_safe(p, p0, &pulse_pool, head) {
105ebacd801SBjoern A. Zeeb list_del(&p->head);
106ebacd801SBjoern A. Zeeb DFS_POOL_STAT_DEC(pulse_allocated);
107ebacd801SBjoern A. Zeeb kfree(p);
108ebacd801SBjoern A. Zeeb }
109ebacd801SBjoern A. Zeeb list_for_each_entry_safe(ps, ps0, &pseq_pool, head) {
110ebacd801SBjoern A. Zeeb list_del(&ps->head);
111ebacd801SBjoern A. Zeeb DFS_POOL_STAT_DEC(pseq_allocated);
112ebacd801SBjoern A. Zeeb kfree(ps);
113ebacd801SBjoern A. Zeeb }
114ebacd801SBjoern A. Zeeb }
115ebacd801SBjoern A. Zeeb spin_unlock_bh(&pool_lock);
116ebacd801SBjoern A. Zeeb }
117ebacd801SBjoern A. Zeeb
pool_put_pulse_elem(struct pulse_elem * pe)118ebacd801SBjoern A. Zeeb static void pool_put_pulse_elem(struct pulse_elem *pe)
119ebacd801SBjoern A. Zeeb {
120ebacd801SBjoern A. Zeeb spin_lock_bh(&pool_lock);
121ebacd801SBjoern A. Zeeb list_add(&pe->head, &pulse_pool);
122ebacd801SBjoern A. Zeeb DFS_POOL_STAT_DEC(pulse_used);
123ebacd801SBjoern A. Zeeb spin_unlock_bh(&pool_lock);
124ebacd801SBjoern A. Zeeb }
125ebacd801SBjoern A. Zeeb
pool_put_pseq_elem(struct pri_sequence * pse)126ebacd801SBjoern A. Zeeb static void pool_put_pseq_elem(struct pri_sequence *pse)
127ebacd801SBjoern A. Zeeb {
128ebacd801SBjoern A. Zeeb spin_lock_bh(&pool_lock);
129ebacd801SBjoern A. Zeeb list_add(&pse->head, &pseq_pool);
130ebacd801SBjoern A. Zeeb DFS_POOL_STAT_DEC(pseq_used);
131ebacd801SBjoern A. Zeeb spin_unlock_bh(&pool_lock);
132ebacd801SBjoern A. Zeeb }
133ebacd801SBjoern A. Zeeb
pool_get_pseq_elem(void)134ebacd801SBjoern A. Zeeb static struct pri_sequence *pool_get_pseq_elem(void)
135ebacd801SBjoern A. Zeeb {
136ebacd801SBjoern A. Zeeb struct pri_sequence *pse = NULL;
137ebacd801SBjoern A. Zeeb spin_lock_bh(&pool_lock);
138ebacd801SBjoern A. Zeeb if (!list_empty(&pseq_pool)) {
139ebacd801SBjoern A. Zeeb pse = list_first_entry(&pseq_pool, struct pri_sequence, head);
140ebacd801SBjoern A. Zeeb list_del(&pse->head);
141ebacd801SBjoern A. Zeeb DFS_POOL_STAT_INC(pseq_used);
142ebacd801SBjoern A. Zeeb }
143ebacd801SBjoern A. Zeeb spin_unlock_bh(&pool_lock);
144ebacd801SBjoern A. Zeeb return pse;
145ebacd801SBjoern A. Zeeb }
146ebacd801SBjoern A. Zeeb
pool_get_pulse_elem(void)147ebacd801SBjoern A. Zeeb static struct pulse_elem *pool_get_pulse_elem(void)
148ebacd801SBjoern A. Zeeb {
149ebacd801SBjoern A. Zeeb struct pulse_elem *pe = NULL;
150ebacd801SBjoern A. Zeeb spin_lock_bh(&pool_lock);
151ebacd801SBjoern A. Zeeb if (!list_empty(&pulse_pool)) {
152ebacd801SBjoern A. Zeeb pe = list_first_entry(&pulse_pool, struct pulse_elem, head);
153ebacd801SBjoern A. Zeeb list_del(&pe->head);
154ebacd801SBjoern A. Zeeb DFS_POOL_STAT_INC(pulse_used);
155ebacd801SBjoern A. Zeeb }
156ebacd801SBjoern A. Zeeb spin_unlock_bh(&pool_lock);
157ebacd801SBjoern A. Zeeb return pe;
158ebacd801SBjoern A. Zeeb }
159ebacd801SBjoern A. Zeeb
pulse_queue_get_tail(struct pri_detector * pde)160ebacd801SBjoern A. Zeeb static struct pulse_elem *pulse_queue_get_tail(struct pri_detector *pde)
161ebacd801SBjoern A. Zeeb {
162ebacd801SBjoern A. Zeeb struct list_head *l = &pde->pulses;
163ebacd801SBjoern A. Zeeb if (list_empty(l))
164ebacd801SBjoern A. Zeeb return NULL;
165ebacd801SBjoern A. Zeeb return list_entry(l->prev, struct pulse_elem, head);
166ebacd801SBjoern A. Zeeb }
167ebacd801SBjoern A. Zeeb
pulse_queue_dequeue(struct pri_detector * pde)168ebacd801SBjoern A. Zeeb static bool pulse_queue_dequeue(struct pri_detector *pde)
169ebacd801SBjoern A. Zeeb {
170ebacd801SBjoern A. Zeeb struct pulse_elem *p = pulse_queue_get_tail(pde);
171ebacd801SBjoern A. Zeeb if (p != NULL) {
172ebacd801SBjoern A. Zeeb list_del_init(&p->head);
173ebacd801SBjoern A. Zeeb pde->count--;
174ebacd801SBjoern A. Zeeb /* give it back to pool */
175ebacd801SBjoern A. Zeeb pool_put_pulse_elem(p);
176ebacd801SBjoern A. Zeeb }
177ebacd801SBjoern A. Zeeb return (pde->count > 0);
178ebacd801SBjoern A. Zeeb }
179ebacd801SBjoern A. Zeeb
180ebacd801SBjoern A. Zeeb /* remove pulses older than window */
pulse_queue_check_window(struct pri_detector * pde)181ebacd801SBjoern A. Zeeb static void pulse_queue_check_window(struct pri_detector *pde)
182ebacd801SBjoern A. Zeeb {
183ebacd801SBjoern A. Zeeb u64 min_valid_ts;
184ebacd801SBjoern A. Zeeb struct pulse_elem *p;
185ebacd801SBjoern A. Zeeb
186ebacd801SBjoern A. Zeeb /* there is no delta time with less than 2 pulses */
187ebacd801SBjoern A. Zeeb if (pde->count < 2)
188ebacd801SBjoern A. Zeeb return;
189ebacd801SBjoern A. Zeeb
190ebacd801SBjoern A. Zeeb if (pde->last_ts <= pde->window_size)
191ebacd801SBjoern A. Zeeb return;
192ebacd801SBjoern A. Zeeb
193ebacd801SBjoern A. Zeeb min_valid_ts = pde->last_ts - pde->window_size;
194ebacd801SBjoern A. Zeeb while ((p = pulse_queue_get_tail(pde)) != NULL) {
195ebacd801SBjoern A. Zeeb if (p->ts >= min_valid_ts)
196ebacd801SBjoern A. Zeeb return;
197ebacd801SBjoern A. Zeeb pulse_queue_dequeue(pde);
198ebacd801SBjoern A. Zeeb }
199ebacd801SBjoern A. Zeeb }
200ebacd801SBjoern A. Zeeb
pulse_queue_enqueue(struct pri_detector * pde,u64 ts)201ebacd801SBjoern A. Zeeb static bool pulse_queue_enqueue(struct pri_detector *pde, u64 ts)
202ebacd801SBjoern A. Zeeb {
203ebacd801SBjoern A. Zeeb struct pulse_elem *p = pool_get_pulse_elem();
204ebacd801SBjoern A. Zeeb if (p == NULL) {
205ebacd801SBjoern A. Zeeb p = kmalloc(sizeof(*p), GFP_ATOMIC);
206ebacd801SBjoern A. Zeeb if (p == NULL) {
207ebacd801SBjoern A. Zeeb DFS_POOL_STAT_INC(pulse_alloc_error);
208ebacd801SBjoern A. Zeeb return false;
209ebacd801SBjoern A. Zeeb }
210ebacd801SBjoern A. Zeeb DFS_POOL_STAT_INC(pulse_allocated);
211ebacd801SBjoern A. Zeeb DFS_POOL_STAT_INC(pulse_used);
212ebacd801SBjoern A. Zeeb }
213ebacd801SBjoern A. Zeeb INIT_LIST_HEAD(&p->head);
214ebacd801SBjoern A. Zeeb p->ts = ts;
215ebacd801SBjoern A. Zeeb list_add(&p->head, &pde->pulses);
216ebacd801SBjoern A. Zeeb pde->count++;
217ebacd801SBjoern A. Zeeb pde->last_ts = ts;
218ebacd801SBjoern A. Zeeb pulse_queue_check_window(pde);
219ebacd801SBjoern A. Zeeb if (pde->count >= pde->max_count)
220ebacd801SBjoern A. Zeeb pulse_queue_dequeue(pde);
221ebacd801SBjoern A. Zeeb return true;
222ebacd801SBjoern A. Zeeb }
223ebacd801SBjoern A. Zeeb
pseq_handler_create_sequences(struct pri_detector * pde,u64 ts,u32 min_count)224ebacd801SBjoern A. Zeeb static bool pseq_handler_create_sequences(struct pri_detector *pde,
225ebacd801SBjoern A. Zeeb u64 ts, u32 min_count)
226ebacd801SBjoern A. Zeeb {
227ebacd801SBjoern A. Zeeb struct pulse_elem *p;
228ebacd801SBjoern A. Zeeb list_for_each_entry(p, &pde->pulses, head) {
229ebacd801SBjoern A. Zeeb struct pri_sequence ps, *new_ps;
230ebacd801SBjoern A. Zeeb struct pulse_elem *p2;
231ebacd801SBjoern A. Zeeb u32 tmp_false_count;
232ebacd801SBjoern A. Zeeb u64 min_valid_ts;
233ebacd801SBjoern A. Zeeb u32 delta_ts = ts - p->ts;
234ebacd801SBjoern A. Zeeb
235ebacd801SBjoern A. Zeeb if (delta_ts < pde->rs->pri_min)
236ebacd801SBjoern A. Zeeb /* ignore too small pri */
237ebacd801SBjoern A. Zeeb continue;
238ebacd801SBjoern A. Zeeb
239ebacd801SBjoern A. Zeeb if (delta_ts > pde->rs->pri_max)
240ebacd801SBjoern A. Zeeb /* stop on too large pri (sorted list) */
241ebacd801SBjoern A. Zeeb break;
242ebacd801SBjoern A. Zeeb
243ebacd801SBjoern A. Zeeb /* build a new sequence with new potential pri */
244ebacd801SBjoern A. Zeeb ps.count = 2;
245ebacd801SBjoern A. Zeeb ps.count_falses = 0;
246ebacd801SBjoern A. Zeeb ps.first_ts = p->ts;
247ebacd801SBjoern A. Zeeb ps.last_ts = ts;
248ebacd801SBjoern A. Zeeb ps.pri = GET_PRI_TO_USE(pde->rs->pri_min,
249ebacd801SBjoern A. Zeeb pde->rs->pri_max, ts - p->ts);
250ebacd801SBjoern A. Zeeb ps.dur = ps.pri * (pde->rs->ppb - 1)
251ebacd801SBjoern A. Zeeb + 2 * pde->rs->max_pri_tolerance;
252ebacd801SBjoern A. Zeeb
253ebacd801SBjoern A. Zeeb p2 = p;
254ebacd801SBjoern A. Zeeb tmp_false_count = 0;
255ebacd801SBjoern A. Zeeb min_valid_ts = ts - ps.dur;
256ebacd801SBjoern A. Zeeb /* check which past pulses are candidates for new sequence */
257ebacd801SBjoern A. Zeeb list_for_each_entry_continue(p2, &pde->pulses, head) {
258ebacd801SBjoern A. Zeeb u32 factor;
259ebacd801SBjoern A. Zeeb if (p2->ts < min_valid_ts)
260ebacd801SBjoern A. Zeeb /* stop on crossing window border */
261ebacd801SBjoern A. Zeeb break;
262ebacd801SBjoern A. Zeeb /* check if pulse match (multi)PRI */
263ebacd801SBjoern A. Zeeb factor = pde_get_multiple(ps.last_ts - p2->ts, ps.pri,
264ebacd801SBjoern A. Zeeb pde->rs->max_pri_tolerance);
265ebacd801SBjoern A. Zeeb if (factor > 0) {
266ebacd801SBjoern A. Zeeb ps.count++;
267ebacd801SBjoern A. Zeeb ps.first_ts = p2->ts;
268ebacd801SBjoern A. Zeeb /*
269ebacd801SBjoern A. Zeeb * on match, add the intermediate falses
270ebacd801SBjoern A. Zeeb * and reset counter
271ebacd801SBjoern A. Zeeb */
272ebacd801SBjoern A. Zeeb ps.count_falses += tmp_false_count;
273ebacd801SBjoern A. Zeeb tmp_false_count = 0;
274ebacd801SBjoern A. Zeeb } else {
275ebacd801SBjoern A. Zeeb /* this is a potential false one */
276ebacd801SBjoern A. Zeeb tmp_false_count++;
277ebacd801SBjoern A. Zeeb }
278ebacd801SBjoern A. Zeeb }
279ebacd801SBjoern A. Zeeb if (ps.count <= min_count)
280ebacd801SBjoern A. Zeeb /* did not reach minimum count, drop sequence */
281ebacd801SBjoern A. Zeeb continue;
282ebacd801SBjoern A. Zeeb
283ebacd801SBjoern A. Zeeb /* this is a valid one, add it */
284ebacd801SBjoern A. Zeeb ps.deadline_ts = ps.first_ts + ps.dur;
285ebacd801SBjoern A. Zeeb new_ps = pool_get_pseq_elem();
286ebacd801SBjoern A. Zeeb if (new_ps == NULL) {
287ebacd801SBjoern A. Zeeb new_ps = kmalloc(sizeof(*new_ps), GFP_ATOMIC);
288ebacd801SBjoern A. Zeeb if (new_ps == NULL) {
289ebacd801SBjoern A. Zeeb DFS_POOL_STAT_INC(pseq_alloc_error);
290ebacd801SBjoern A. Zeeb return false;
291ebacd801SBjoern A. Zeeb }
292ebacd801SBjoern A. Zeeb DFS_POOL_STAT_INC(pseq_allocated);
293ebacd801SBjoern A. Zeeb DFS_POOL_STAT_INC(pseq_used);
294ebacd801SBjoern A. Zeeb }
295ebacd801SBjoern A. Zeeb memcpy(new_ps, &ps, sizeof(ps));
296ebacd801SBjoern A. Zeeb INIT_LIST_HEAD(&new_ps->head);
297ebacd801SBjoern A. Zeeb list_add(&new_ps->head, &pde->sequences);
298ebacd801SBjoern A. Zeeb }
299ebacd801SBjoern A. Zeeb return true;
300ebacd801SBjoern A. Zeeb }
301ebacd801SBjoern A. Zeeb
302ebacd801SBjoern A. Zeeb /* check new ts and add to all matching existing sequences */
303ebacd801SBjoern A. Zeeb static u32
pseq_handler_add_to_existing_seqs(struct pri_detector * pde,u64 ts)304ebacd801SBjoern A. Zeeb pseq_handler_add_to_existing_seqs(struct pri_detector *pde, u64 ts)
305ebacd801SBjoern A. Zeeb {
306ebacd801SBjoern A. Zeeb u32 max_count = 0;
307ebacd801SBjoern A. Zeeb struct pri_sequence *ps, *ps2;
308ebacd801SBjoern A. Zeeb list_for_each_entry_safe(ps, ps2, &pde->sequences, head) {
309ebacd801SBjoern A. Zeeb u32 delta_ts;
310ebacd801SBjoern A. Zeeb u32 factor;
311ebacd801SBjoern A. Zeeb
312ebacd801SBjoern A. Zeeb /* first ensure that sequence is within window */
313ebacd801SBjoern A. Zeeb if (ts > ps->deadline_ts) {
314ebacd801SBjoern A. Zeeb list_del_init(&ps->head);
315ebacd801SBjoern A. Zeeb pool_put_pseq_elem(ps);
316ebacd801SBjoern A. Zeeb continue;
317ebacd801SBjoern A. Zeeb }
318ebacd801SBjoern A. Zeeb
319ebacd801SBjoern A. Zeeb delta_ts = ts - ps->last_ts;
320ebacd801SBjoern A. Zeeb factor = pde_get_multiple(delta_ts, ps->pri,
321ebacd801SBjoern A. Zeeb pde->rs->max_pri_tolerance);
322ebacd801SBjoern A. Zeeb if (factor > 0) {
323ebacd801SBjoern A. Zeeb ps->last_ts = ts;
324ebacd801SBjoern A. Zeeb ps->count++;
325ebacd801SBjoern A. Zeeb
326ebacd801SBjoern A. Zeeb if (max_count < ps->count)
327ebacd801SBjoern A. Zeeb max_count = ps->count;
328ebacd801SBjoern A. Zeeb } else {
329ebacd801SBjoern A. Zeeb ps->count_falses++;
330ebacd801SBjoern A. Zeeb }
331ebacd801SBjoern A. Zeeb }
332ebacd801SBjoern A. Zeeb return max_count;
333ebacd801SBjoern A. Zeeb }
334ebacd801SBjoern A. Zeeb
335ebacd801SBjoern A. Zeeb static struct pri_sequence *
pseq_handler_check_detection(struct pri_detector * pde)336ebacd801SBjoern A. Zeeb pseq_handler_check_detection(struct pri_detector *pde)
337ebacd801SBjoern A. Zeeb {
338ebacd801SBjoern A. Zeeb struct pri_sequence *ps;
339ebacd801SBjoern A. Zeeb
340ebacd801SBjoern A. Zeeb if (list_empty(&pde->sequences))
341ebacd801SBjoern A. Zeeb return NULL;
342ebacd801SBjoern A. Zeeb
343ebacd801SBjoern A. Zeeb list_for_each_entry(ps, &pde->sequences, head) {
344ebacd801SBjoern A. Zeeb /*
345ebacd801SBjoern A. Zeeb * we assume to have enough matching confidence if we
346ebacd801SBjoern A. Zeeb * 1) have enough pulses
347ebacd801SBjoern A. Zeeb * 2) have more matching than false pulses
348ebacd801SBjoern A. Zeeb */
349ebacd801SBjoern A. Zeeb if ((ps->count >= pde->rs->ppb_thresh) &&
350ebacd801SBjoern A. Zeeb (ps->count * pde->rs->num_pri >= ps->count_falses))
351ebacd801SBjoern A. Zeeb return ps;
352ebacd801SBjoern A. Zeeb }
353ebacd801SBjoern A. Zeeb return NULL;
354ebacd801SBjoern A. Zeeb }
355ebacd801SBjoern A. Zeeb
356ebacd801SBjoern A. Zeeb
357ebacd801SBjoern A. Zeeb /* free pulse queue and sequences list and give objects back to pools */
pri_detector_reset(struct pri_detector * pde,u64 ts)358ebacd801SBjoern A. Zeeb static void pri_detector_reset(struct pri_detector *pde, u64 ts)
359ebacd801SBjoern A. Zeeb {
360ebacd801SBjoern A. Zeeb struct pri_sequence *ps, *ps0;
361ebacd801SBjoern A. Zeeb struct pulse_elem *p, *p0;
362ebacd801SBjoern A. Zeeb list_for_each_entry_safe(ps, ps0, &pde->sequences, head) {
363ebacd801SBjoern A. Zeeb list_del_init(&ps->head);
364ebacd801SBjoern A. Zeeb pool_put_pseq_elem(ps);
365ebacd801SBjoern A. Zeeb }
366ebacd801SBjoern A. Zeeb list_for_each_entry_safe(p, p0, &pde->pulses, head) {
367ebacd801SBjoern A. Zeeb list_del_init(&p->head);
368ebacd801SBjoern A. Zeeb pool_put_pulse_elem(p);
369ebacd801SBjoern A. Zeeb }
370ebacd801SBjoern A. Zeeb pde->count = 0;
371ebacd801SBjoern A. Zeeb pde->last_ts = ts;
372ebacd801SBjoern A. Zeeb }
373ebacd801SBjoern A. Zeeb
pri_detector_exit(struct pri_detector * de)374ebacd801SBjoern A. Zeeb static void pri_detector_exit(struct pri_detector *de)
375ebacd801SBjoern A. Zeeb {
376ebacd801SBjoern A. Zeeb pri_detector_reset(de, 0);
377ebacd801SBjoern A. Zeeb pool_deregister_ref();
378ebacd801SBjoern A. Zeeb kfree(de);
379ebacd801SBjoern A. Zeeb }
380ebacd801SBjoern A. Zeeb
pri_detector_add_pulse(struct pri_detector * de,struct pulse_event * event)381ebacd801SBjoern A. Zeeb static struct pri_sequence *pri_detector_add_pulse(struct pri_detector *de,
382ebacd801SBjoern A. Zeeb struct pulse_event *event)
383ebacd801SBjoern A. Zeeb {
384ebacd801SBjoern A. Zeeb u32 max_updated_seq;
385ebacd801SBjoern A. Zeeb struct pri_sequence *ps;
386ebacd801SBjoern A. Zeeb u64 ts = event->ts;
387ebacd801SBjoern A. Zeeb const struct radar_detector_specs *rs = de->rs;
388ebacd801SBjoern A. Zeeb
389ebacd801SBjoern A. Zeeb /* ignore pulses not within width range */
390ebacd801SBjoern A. Zeeb if ((rs->width_min > event->width) || (rs->width_max < event->width))
391ebacd801SBjoern A. Zeeb return NULL;
392ebacd801SBjoern A. Zeeb
393ebacd801SBjoern A. Zeeb if ((ts - de->last_ts) < rs->max_pri_tolerance)
394ebacd801SBjoern A. Zeeb /* if delta to last pulse is too short, don't use this pulse */
395ebacd801SBjoern A. Zeeb return NULL;
396ebacd801SBjoern A. Zeeb /* radar detector spec needs chirp, but not detected */
397ebacd801SBjoern A. Zeeb if (rs->chirp && rs->chirp != event->chirp)
398ebacd801SBjoern A. Zeeb return NULL;
399ebacd801SBjoern A. Zeeb
400ebacd801SBjoern A. Zeeb de->last_ts = ts;
401ebacd801SBjoern A. Zeeb
402ebacd801SBjoern A. Zeeb max_updated_seq = pseq_handler_add_to_existing_seqs(de, ts);
403ebacd801SBjoern A. Zeeb
404ebacd801SBjoern A. Zeeb if (!pseq_handler_create_sequences(de, ts, max_updated_seq)) {
405ebacd801SBjoern A. Zeeb pri_detector_reset(de, ts);
406ebacd801SBjoern A. Zeeb return NULL;
407ebacd801SBjoern A. Zeeb }
408ebacd801SBjoern A. Zeeb
409ebacd801SBjoern A. Zeeb ps = pseq_handler_check_detection(de);
410ebacd801SBjoern A. Zeeb
411ebacd801SBjoern A. Zeeb if (ps == NULL)
412ebacd801SBjoern A. Zeeb pulse_queue_enqueue(de, ts);
413ebacd801SBjoern A. Zeeb
414ebacd801SBjoern A. Zeeb return ps;
415ebacd801SBjoern A. Zeeb }
416ebacd801SBjoern A. Zeeb
pri_detector_init(const struct radar_detector_specs * rs)417ebacd801SBjoern A. Zeeb struct pri_detector *pri_detector_init(const struct radar_detector_specs *rs)
418ebacd801SBjoern A. Zeeb {
419ebacd801SBjoern A. Zeeb struct pri_detector *de;
420ebacd801SBjoern A. Zeeb
421ebacd801SBjoern A. Zeeb de = kzalloc(sizeof(*de), GFP_ATOMIC);
422ebacd801SBjoern A. Zeeb if (de == NULL)
423ebacd801SBjoern A. Zeeb return NULL;
424ebacd801SBjoern A. Zeeb de->exit = pri_detector_exit;
425ebacd801SBjoern A. Zeeb de->add_pulse = pri_detector_add_pulse;
426ebacd801SBjoern A. Zeeb de->reset = pri_detector_reset;
427ebacd801SBjoern A. Zeeb
428ebacd801SBjoern A. Zeeb INIT_LIST_HEAD(&de->sequences);
429ebacd801SBjoern A. Zeeb INIT_LIST_HEAD(&de->pulses);
430ebacd801SBjoern A. Zeeb de->window_size = rs->pri_max * rs->ppb * rs->num_pri;
431ebacd801SBjoern A. Zeeb de->max_count = rs->ppb * 2;
432ebacd801SBjoern A. Zeeb de->rs = rs;
433ebacd801SBjoern A. Zeeb
434ebacd801SBjoern A. Zeeb pool_register_ref();
435ebacd801SBjoern A. Zeeb return de;
436ebacd801SBjoern A. Zeeb }
437