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